概念
@property
,@synthesize
都是编译器指令,在源文件被编译时,会被替换为相应的代码。@property
提供了一种封装对象数据的方法。@property
被声明在类的接口中:
@interface XYZPerson : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end
或者私有的接口扩展中
@interface XYZPerson ()
@property NSString *firstName;
@property NSString *lastName;
@end
编译器会默认自动生成get和set方法,get方法和@property同名,set方法则是set加property名首字母大写。编译器还会默认生成一个成员变量,命名为property名前加下划线,set和get方法操作的就是这个变量。可以通过@synthesize修改默认的变量名,如:
@synthesize firstName = ivar_firstName;
如果不重新指定变量名,则变量会与property同名,如:
@synthesize firstName;
可以自定义实现property的存取方法,如果存取方法都实现了的话,则编译器默认不会再生成成员变量。在Xcode4.4之前,必须在implementation中声明@synthesize编译器才会实现相应存取方法。
属性
可以在@property
后加一对括号,里面可以定义一些property的属性。属性告诉编译器如何生成相应的访问方法。
- getter/setter,可以在括号中对get和set方法进行重命名,格式为(getter=xxx, setter=setXxx)
- readonly/readwrite,readonly只会生成get方法,readwrite则会生成get和set方法,readwrite是默认值。
- atomic/nonatomic,默认值为atomic,此时默认生成的访问方法可以确保在多线程情况下,get和set方法可以正确执行,因为atomic访问方法的实现是私有的,所以若自定义访问方法,则只能使用nonatomic属性,若默认生成的存取方法使用nonatomic属性,则不会有atomic的保护,只是简单地存取变量,因此,使用nonatomic属性会使存取速度更快,如果不是多线程编程的话,最好使用nonatomic属性。
- strong/retain/weak/assign/unsafe_unretained,这是一组关于property对应变量的内存管理的属性,有关内存管理,要对MRC和ARC,以及基本数据类型和对象数据类型分情况说明。
strong和weak是随ARC引入的新的property属性,它们都是修饰对象数据类型变量的property。strong修饰的对应变量指针引用的对象不会释放,相当于对象执行retain,而weak不会影响对象的释放,即使有很多weak指针变量引用对象,当该对象没有strong变量指针引用时,该对象就会释放,weak指针的关键点是,当其指向的对象释放时,该指针不会再存储之前对象的堆内存地址,而是置空设值为nil。基本可以认为strong与retain是等价的。strong,weak以及retain是不能修饰基本数据类型的,否则编译器会报错。
assign主要用于修饰基本数据类型,当用assign修饰对象数据类型的指针变量时,与weak相似的地方是该指针变量也不持有对象,它与weak最大的不同之处是,其不会清空指针变量中的内存地址,这样当其指向的对象在别处释放时,很有可能出问题。在Cocoa和Cocoa Touch框架中有一些类是不支持weak的,这时使用unsafe_unretained修饰,其效果与assign修饰对象类型变量是一样的。
与property的属性对应的,ARC也引入了变量修饰符__strong、__weak、__unsafe_unretained、__autoreleasing,__strong是默认的修饰符,所以大部分变量声明并不需要写出修饰符,前面3个变量修饰符效果都与对应的property属性相同。而__autoreleasing表示在autorelease pool中自动释放对象的引用,和MRC时代autorelease的用法相同。以下两行代码等同:
// MRC
NSString *str = [[[NSString alloc] initWithFormat:@""] autorelease];
// ARC
NSString *__autoreleasing str = [[NSString alloc] initWithFormat:@"hehe"];
__autorelease主要用在参数传递返回值和引用(id *)传递参数的情况,比如iOS中常用的传递NSError指针的情况
-(BOOL)performOperationWithError:(NSError * __autoreleasing *)error;
因为默认NSError *error
声明的是__strong的引用,所以编译器会默认进行转换
NSError * __strong error;
NSError * __autoreleasing tmp = error;
BOOL OK = [myObject performOperationWithError:&tmp];
error = tmp;
if (!OK) {
// Report the error.
}
所以在声明时,我们可以直接将error变量声明为__autoreleasing。
- copy
copy只能修饰对象数据类型,如果一个property被copy修饰,那么赋值到这个property的对象,应该是原有对象的一份拷贝。copy修饰的对象数据类型必须实现了NSCopying协议,并且实现了其中的copyWithZone:方法,否则会因为找不到该方法而crash。copy修饰的property对应的变量用__strong修饰。一般在不可变类的property中用copy修饰,比如NSString,NSArray,NSDictionary。因为这些类的可变版本可以赋值给它们,这样可能导致在不确定的情况下,变量被修改。
网友评论