作者:小小伟 | 来源:互联网 | 2023-02-12 13:51
0x01 存取方法是间接的一个例子
上一篇中,我们已经构造了一台“车”。
假设现在我想要随时更换自己喜欢的发动机和轮胎,就要用到“存取方法”。
“存取方法”是用来读取或改变某个对象属性的方法,例如此前见过的setFillColor:就是一个存取方法。
如果我们要对其他对象中的属性进行操作,应该尽量使用对象提供的存取方法,绝对不要直接改变对象里面的值,而应该使用setter方法进行更改。
正确使用“存取方法”将会使程序更加灵活。
0x02 方法的命名规则
1、虽然说方法的命名是自由的,但为了程序的可读性,我们还是应该遵循Cocoa的惯例:
setter方法应根据它所要更改的属性名称来命名,如“set+属性名”;
getter方法只需以其返回的属性名命名即可,即“属性名”。
因为get这个词在Cocoa中有特殊含义,如果get出现在Cocoa的方法名称中,就意味着这个方法会将传递给它的参数作为指针来返回数值。
2、setter方法和getter方法一般是成对出现的,当然也可以不成对出现。如对于只读特性只有getter方法,对于密码特性只有setter方法。
0x03 存取方法的实现
我们先在Car的接口中对添加的setter和getter方法进行声明:
@interface Car : NSObject
{Engine *engine;Tire *tires[4];
}//New Code
- (Engine *) engine;
- (void) setEngine: (Engine *) newEngine;
- (Tire *) tireAtIndex: (int) index;
- (void) setTire: (Tire *) tire atIndex: (int) index;
//New Code- (void) print;
@end // Car
第一对存取方法是为了访问发动机的属性:
- (Engine *) engine;
- (void) setEngine: (Engine *) newEngine;
调用Car对象的engine方法可以访问engine变量,调用setEngine:方法可以更改发动机属性。
下面是这两个方法的实现:
- (Engine *) engine
{return (engine);
} // engine
- (void) setEngine: (Engine *) newEngine
{engine = newEngine;
} // setEngine
我们看到getter方法engine返回的是一个指针,指向Car中的Engine对象。实际上是返回实例变量engine的当前值;
同样的,setter方法setEngine:将newEngine的值赋给engine实例变量。实际上被修改的是engine的指针值。
在Objective-C中所有对象间的交互都是通过指针实现的!
第二对存取方法是为了访问轮胎的属性:
- (void) setTire: (Tire *) tire atIndex: (int) index;
- (Tire *) tireAtIndex: (int) index;
由于汽车的4个轮胎处于4个位置,所以Car对象中必须包含一个表示轮胎位置的数组:
- (void) setTire: (Tire *) tire atIndex: (int) index{if (index <0 || index > 3) {NSLog (@"bad index (%d) in setTire:atIndex:",index);exit (1);
}tires[index] = tire;
} // setTire:atIndex:
- (Tire *) tireAtIndex: (int) index
{if (index <0 || index > 3) {NSLog (@"bad index (%d) in "tireAtIndex:", index);exit (1);
}return (tires[index]);
} // tireAtIndex:
因为tire是一个C风格的数组,对于其索引值是否有效,编译器是可能直接忽略的。所以我们自己得用if条件语句来判断index值是否合理,这是一种防御式编程思想。
setTire:方法的声明格式看起来不太一样,这是因为它拥有两个参数,往后学习会更加理解这种写法。
最后修改main()函数:
int main (int argc, const char * argv[]) {Car *car = [Car new];//事实上可以在Car类中把init方法删掉,因为下面的代码已经做了init方法的工作Engine *engine = [Engine new];[car setEngine: engine];for (int i = 0; i <4; i++) {Tire *tire = [Tire new];[car setTire: tire atIndex: i];}[car print];return (0);
} // main
程序运行结果没有变化,但我们改进了它的内部结构,使程序更加灵活:
I am an engine. Vrooom!
I am a tire. I last a while.
I am a tire. I last a while.
I am a tire. I last a while.
I am a tire. I last a while.
0x04 存取方法的应用
我们已经为Car类添加了存取方法,那换配置就是随心所欲的事了。
现在来为这台车换一个发动机:
@interface Slant6 : Engine
@end // Slant6@implementation Slant6
- (NSString *) description
{return (@"I am a slant- 6. VROOOM!");
} // description
@end // Slant6
创建这台Slant6发动机,继承了Engine超类的属性。但是重写了description方法,输出了新发动机的信息。
接着我们换轮胎:
@interface AllWeatherRadial : Tire
@end // AllWeatherRadial@implementation AllWeatherRadial
- (NSString *) description
{return (@"I am a tire for rain or shine.");
} // description
@end // AllWeatherRadial
最后修改main()函数,这台车使用了全新的发动机和轮胎:
int main (int argc, const char * argv[]) {Car *car = [Car new];for (int i = 0; i <4; i++)
{//New CodeTire *tire = [AllWeatherRadial new];//New Code[car setTire: tire atIndex: i];}//New CodeEngine *engine = [Slant6 new];//New Code[car setEngine: engine];[car print];return (0);
} // main
运行程序后,可以发现输出已经更新为新配置了:
I am a slant- 6. VROOOM!
I am a tire for rain or shine.
I am a tire for rain or shine.
I am a tire for rain or shine.
I am a tire for rain or shine.