@property合成和重写
自动合成
定义一个@property,在编译期间,编译器会生成实例变量、getter方法、setter方法,这些方法、变量是通过自动合成(autosynthesize)的方式生成并添加到类中。实际上,一个类经过编译后,会生成变量列表ivar_list,方法列表method_list,每添加一个属性,在变量列表ivar_list会添加对应的变量,如_name,方法列表method_list中会添加对应的setter方法和getter方法。
动态合成
既然有自动合成,那么相对应的就要有非自动合成,非自动合成又称为动态合成。定义一个属性,默认是自动合成的,默认会生成getter方法和setter方法,自动合成对应的代码是:
@synthesize name = _name;
上面这个代码时编译器自己生成的,不需要我们手动添加的。当然,如果我们需要动态合成,那么就需要自己写如下代码:
@dynamic sex;
上边这样代码就告诉编译器,sex属性的变量名、getter方法、setter方法由开发者自己来添加,编译器无需处理。
当然,这时候你要去新建该对象,并调用该属性,获取值或者赋值。那么在编译的时候是没问题的,同样,运行也是一样没问题的,但当代码执行到这一行的时候,程序就会崩溃,崩溃信息是:
[Student setSex:]: unrecognized selector sent to instance 0x60000217f1a0
也就是说,该对象属性sex没有setterr方法,所以找不到setSex该方法,这也就是动态合成和自动合成的最大区别。动态合成需要开发者自己来写属性的setter、getter方法。当然,使用了@ dynamic后,编译器也不会自动生成遍历,因此你还需自己手动定义相应的变量,如_sex变量。
重写setter、getter方法的注意事项
按照上面需求,我们重写属性name的setter和getter方法一下:
- (void)setName:(NSString *)name
{
NSLog(@"rewrite setter");
_name = name;
}
- (NSString *)name
{
NSLog(@"rewrite getter");
return _name;
}
但是编译器还是会提示错误,错误信息为:
Use of undeclared identifier '_name'; did you mean 'name'?
没有_name变量?我们没声明@dynamic,那么默认就是@ autosynthesize,那为什么没有_name变量呢?而且,当我们把setter或者getter其中一个方法注释掉,只保留一个方法,那就不会有错误的,为什么呢?
当然,还是编译器做了一些处理。对于一个可读写的属性来说,当我们重写了它的setter、getter方法时,编译器会认为开发者想手动管理@property,此时会将@property作为@dynamic来处理,因此也就不会自动生成变量了。当然,也有解决办法,就是将显示的属性和一个变量绑定:
@synthesize name = _name;
这样就不会出错了。如果一个属性是只读的时候,重写getter方法时,编译器也会认为该属性是@dynamic。所以各位在重写getter、setter方法时,注意下是否有编译的问题。
getter方法千万不可用self
有经验的开发者都应该知道这一点,在getter方法中是不能使用self的,比如:
- (NSString *)name
{
NSLog(@"rewrite getter");
return self.name; // 错误的写法,会造成死循环
}
原因代码注释中已经写了,这样会造成死循环。这里需要注意的是:self.name实际上就是执行了属性name的getter方法,getter方法中又调用了self.name, 会一直递归调用,直到程序崩溃。
网友评论