不解

【iOS】详解Objective-C的@property属性定义方法及其nonatomic/weak

大多数对象都需要对某些信息保持跟踪,因此对象都包含一些存储信息的数据,Objective-C通过定义公共的属性来对外提供数据。它的属性定义方法如下:

@interface XYZPersion : NSObject

@property NSString *firstName;

@property NSString *lastName;

@end


面向对象编程的一个基本原则是:对象应该隐藏公共接口背后的内部工作,应该通过行为方法来访问一个对象的属性,而不是直接尝试访问内部数据。因此,我们需要用访问函数去获得或设置属性值,如:

NSString *firstName = [somePerson firstName];

[somePerson setFirstName:@"AISeminar.cn"];

默认,XCode的编译器会自动为你合成访问函数。因此,你只需要再类中使用@property声明一个属性,无需其他操作。


默认情况,一个可读写属性的值将被保存在一个对象变量中。它的内存在对象创建(alloc)时分配,并在对象销毁(dealloc)时释放。如果你没有指定,合成的对象变量名和属性值相似,只是前面加了“_”前缀。如firstName的属性,对应合成的变量名为_firstName。

虽然最好的方式是通过访问函数或“.”语法访问变量,但在对象的方法实现中,仍可以直接访问此属性的变量,“_”可以让你明显的知道你访问的是属性的对象变量。一般情况下,我们应该尽量避免直接使用变量,而是通过“self.”等getter、setter方法在对象函数实现中访问属性。例外的情况是:初始化、销毁或重载了访问方法等,这些函数中应该直接使用变量自身【jink2005:不明白为什么?】。


如果需要自定义属性的变量名,我们需要在类的实现中,显示的调用@synthesize来指定。如下:

@implementation XYZPerson

@synthesize lastName = familyName;

@end

注意:如果你调用了@synthesize而没有指定名字,则对象变量名会和你的属性名相同,没有“_”。


合成的访问函数遵循下面的命名规则:

* getter方法和属性使用相同的名字:如firstName的getter方法还是firstName。

* setter方法以“set”开头,后接属性名,属性名第一个字母变为大写:如firstName的setter方法为setFirstName。


调用访问函数的简单替代语法“.”

Objective-C允许使用C++中习惯的“.”操作调用属性的getter/setter方法,如下:

NSString *firstName = somePerson.firstName;

somePerson.firstName = @"AISeminar.cn";

这样的书写其实会被替换为上面的getter/setter方法去执行。例如,如果firstName属性时readonly,你使用somePerson.firstName=@"AISeminar.cn",在编译时就会报错。


@property可以设置的一些attributes

* readonly:设置后将不允许通过setter方法修改属性,即编译器将只合成getter方法,不创建setter方法。

  例:@property (readonly) NSString *fullName;

* readwrite:默认设置,和readonly相对,无需显示设置,即getter、setter都会创建。

* setter=, getter=:为访问函数自定义函数名字,如为一个BOOL值的属性定义getter方法为isFinished,如下:

  @property (getter=isFinished) BOOL finished;

  如果需要设置多个属性,可以通过“,”分隔,如下:

  @property (readonly, getter=isFinished) BOOL finished;

  这时,编译器只会合成一个名为isFinished的getter方法,无setFinished。

* atomic:默认设置,代表属性的getter/setter操作具有原子性,即多线程安全。因为内部实现和同步,atomic访问方法是私有的,因此,我们不能将系统实现的getter或setter方法和自己实现的setter或getter方法组合在一起实现,这样编译时会有警告。

* nonatomic:设置则属性的访问方法不具有多线程安全性,但是访问会更快。

* strong:默认设置,无需显示设置。表示强引用该属性对象,这一属性对象属于此类对象,对象的销毁将导致该属性对象的销毁。

* weak:弱引用。表示类对象只是需要用此属性对象,但不是必须。类对象销毁后,在属性对象还可能存在。如UITableView的代理,可能是某个ViewController,table释放时,ViewController有可能还存在而不能释放。

  @property (weak) id delegate;

  详解见本博:【iOS】图解Objective-C中内存的管理机制:强引用与弱引用

* unsafe_unretained:对无法使用weak引用的类如NSTextView、NSFont和NSColorSpace等,使用。但是,当对应对象被销毁时,这样的对象无法自动设为nil,因此是unsafe的。

* copy:必定对应强引用,属性需要支持NSCopying协议。这样的属性在赋值时,会将对应内容以拷贝方式(新建内存)复制对应对象的内容。如下:

  @property (copy) NSString *firstName;

  @property NSString *testName;

  使用:

  NSMutableString *nameStr = [NSMutableString stringWithString:@"Jink2005"];

  self.firstName = nameStr;

  self.testName = nameStr;

  [nameStr appendString:@"@aiseminar.cn"];

  结果:

  self.firstName的值仍是Jink2005,而self.testName的值则为Jink2005@aiseminar.cn。

* retain:参考文档中没有详细介绍,感觉应该和strong相当,让对应的对象引用计数加1。

* assign:参考文档中没有详细介绍,值的直接赋值,感觉和weak相当。

  @property(nonatomic, assign) id<UITableViewDelegate> delegate;


注意:如果自己实现了一个可读写属性的setter和getter方法、或一个只读属性的getter方法,那么编译器不会再为我们自动创建对象变量,如果需要创建,需要调用:

@synthesize xxProperty = _xxProperty;


jink2005翻译自:

Encapsulating Data

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/EncapsulatingData/EncapsulatingData.html

Programming with Objective-C

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html


同步自网易博客 (查看原文)

评论