0x01 属性声明语法
通过在@property后的括号内添加属性特质参数,可以影响存取方法的生成。
声明property的语法为:@property (参数1,参数2,...) 类型 名字
@interface Test : NSObject
// 括号内添加属性特质进行限制
@property(nonatomic, readwrite, copy) NSString *name;
@end
0x02 属性参数
属性参数的分类
属性参数主要可以分为三类:
- 读写语义:readwrite,readonly,getter,setter
- 内存管理语义:assign,retain,copy,unsafe_unretained
- 拥有者属性:weak,strong
- 原子性: atomic,nonatomic
属性参数的语义
读写语义
- readwrite:默认的属性,变量可读可写,编译器自动生成setter / getter方法;
- readonly:变量只读,只生成getter方法没有setter方法。但是我们可以自己补上setter方法来修改变量。
以下是readonly属性验证程序:
#import <Foundation/Foundation.h>
#import "Tire.h"
@interface AllWeatherRadial : Tire
@property (readonly) float rainHandling;
-(float)setRainHandling:(float)rainHandling; //手动补充setter方法声明
//由于是readonly属性所以必须先在接口中声明
//否则虽然编译时不会报错
//但程序运行过程中如果对rainHandling赋值,会因为找不到setter方法而崩溃
@property float snowHandling; //snowHandling默认是readwrite属性
@end // AllWeatherRadial.h
//----------------------------------------------------------------------------
//AllWeatherRadial.m
#import "AllWeatherRadial.h"
@implementation AllWeatherRadial
@synthesize rainHandling = _rainHandling; //Xcode 4.5后的编译器并不需要这行代码
-(float)setRainHandling: (float)rh
{
_rainHandling = rh; //注意这里必须使用变量名_rainHandling,记住rainHandling是属性名
} //rainHandling的setter方法实现
...
//main.m文件中的代码不需要修改
readwrite 和 readonly是两个互斥属性。
- getter = < gettername >, setter = < settername >: 可以选择性的在括号里直接指定存取方法的方法名,但有可能会破坏健/值规则。因此如非必要,尽量避免使用。在此不展开详述。
内存管理语义
- assign:默认属性类型,是最简单的赋值特性,它不会对索引计数(Reference Counting)进行更改。变量的setter方法直接赋值,而不进行retain操作:
@property NSString *str;
//-----------------------------------------------
-(void)setStr:(NSString*)value{
str=value;
}
- retain:变量的setter方法释放(release)旧的对象,然后将旧对象的值赋予输入对象,再将输入对象的索引计数增加1(retain):
@property (retain) NSString *str;
//----------------------------------------
-(void)setStr:(NSString*) v{
if(v != str){
[str release];
str=[v retain];
}
}
- copy:变量的setter方法进行Copy操作,与retain一样,建立一个索引计数为1的对象,释放掉旧对象:
@property (copy) NSString *str;
//-------------------------------------------
-(void)setStr:(NSString*) v{
if(v != str){
[str release];
str=[v copy];
}
}
- unsafe_unretained:等效于用__unsafe_unretaind关键字声明的变量,在iOS 5.0之前的系统用该属性代替weak来使用。
语意特性的使用方法:
- 只要是值类型、简单类型的类型,如NSInteger、CGPoint、CGFloat,以及C数据类型int、float、double,bool等,都应使用assign;
- 对于含有可深复制子类的对象,比如说NSArray、NSSet、NSDictionary、NSData、NSString等等,都应该使用copy特性;
- 对于NSMutableArray之类的可变类型,不能够使用Copy特性,否则初始化会出现错误;
- 至于其他的NSObject对象,非ARC环境下都应该使用retain来进行操作,这也是绝大多数所使用的情况。
拥有者特性
对于ARC来说,上一节中所说的getter语意特性(assign、retain、copy)将被拥有者特性所代替。
- strong:strong相当于getter语意特性中的retain,strong特性的属性将会成为对象的拥有者。这个特性称之为强引用:
@property(strong) MyClass *myObject;
//Equal to:@property(retain) MyClass *myObject;
- weak:它声明的属性不会拥有这个对象的所有权,如果弱引用指向的对象被释放的话,弱引用的对象会被自动设置为nil。weak比assign多一个自动把对象置为nil的步骤,称为“归零弱引用”(Zeroing Weak Reference)。
强引用与弱引用的广义区别:
- 强引用也就是我们通常所讲的引用,其存亡直接决定了所指对象的存亡。如果不存在指向一个对象的引用,则此对象会被从内存中释放;
- 弱引用除了不决定对象的存亡外,其他与强引用相同。即使一个对象被持有无数个弱引用,只要没有强引用指向他,那么它还是会被清除。弱引用的归零特性为我们提供了保险,避免我们使用失效的引用。
原子特性
- atomic:原子性。原子性是数据库原理里面的一个概念,ACID中的第一个。在多线程中同一个变量可能被多个线程访问甚至更改造成数据污染,因此为了安全,Objective-C中默认是atomic的,会对对象的setter方法加锁,相应的也会付出维护原子性(数据加锁解锁等)的系统资源代价。
- nonatomic: 非原子性。应用中如果不是特殊情况(多线程间的通讯编程),一般还是用nonatomic来修饰变量,不会对其setter方法加锁,以提高多线程并发访问时的性能。
iOS开发的建议:
- 所有属性都声明为nonatomic;
- 尽量避免多线程抢夺同一块资源;
- 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力。
0x03 ARC中的属性关键字
属性特性与内存管理关键字两者是互斥的关系,在启用ARC之后,属性、关键字和对应的所有权关系如下:
属性值 | 关键字 | 所有权 |
---|---|---|
strong | __strong | 有 |
weak | __weak | 无 |
unsafe_unretained | __unsafe_unretained | 无 |
copy | __strong | 有 |
assign | __unsafe_unretained | 无 |
retain | __strong | 有 |
- 属性值一列都是用于声明@property属性声明时用的;
- 编译器编译后会将这些属性值转换成对应的关键字;
- 关键字是用于声明属性列表或普通实例变量指针的。