@property、@synthesize、@dynamic
@property
属性 = getter + setter +成员变量
@property关键字可以自动生成某个成员变量的setter和getter方法的声明
@synthesize
指定一个以下划线 ( _ ) 为前缀、加上属性名的成员变量,在编译期创造,并且由编译器自动生成属性的getter、setter的实现部分
在以前,@property 要和 @synthesize 搭配使用。
后来,编译器引入了property autosynthesis,即属性自动合成。编译器会自动给 @property 添加 @synthesize。
@synthesize propertyName = _propertyName;
这行代码会在编译期间创造一个带下划线的实例变量名,同时使用这个属性生成getter 和 setter 方法。所以现在,我们的代码中已经很少看到 @synthesize 了。
所以看起来,就相当于属性 = getter + setter +成员变量
我们也可以使用@synthesize来指定属性对应的实例变量名,但最好不要这样做,要遵守代码规范。上述编译器会自动编写这些属性所需的方法,这一过程叫做“自动合成”,与之对应的就是“动态合成”。“动态合成”,即需要开发者自己来添加属性的实例变量名,实现getter、setter方法,这时就需要用到 @dynamic。
@dynamic
使用该关键字,编译器就不会为上面这个类自动合成存取方法或实例变量
原子性
原子操作,可以理解为不可被中断的一个或一系列操作
atomic
系统自动生成的setter、getter方法会进行加锁操作,仅保证属性读写安全,且耗性能
nonatomic
系统自动生成的setter、getter方法不会进行加锁操作。
atomic是默认的属性关键字,atomic只是对属性的setter、getter进行加锁,当前线程正在读/写这个属性,其他线程必须在当前线程执行完才能对其进行读/写,atomic仅保证了属性的setter/getter的线程安全,并不能保证使用属性的过程是线程安全的。
例如,如果线程 A 调了 getter,与此同时线程 B 、线程 C 都调了 setter,最后线程 A get 到的值有3种可能:可能是 B、C set 之前原始的值,也可能是 B set 的值,也可能是 C set 的值。同时,最终这个属性的值,可能是 B set 的值,也有可能是 C set 的值。所以atomic并不能保证对象的线程安全。
在实际的iOS开发中,我们经常会对属性进行取值赋值,如果属性关键字是atomic,那就会有大量的加锁解锁,非常耗性能,而实际上,很少会出现多线程调用一个setter、getter,所以一般我们使用的是nonatomic。对于线程安全,我们需要自己在恰当的位置进行加锁解锁来保证。
读写权限
readwrite
其修饰的属性拥有“获取方法”(getter)和"设置方法"(setter)
readonly
其修饰的属性仅拥有"获取方法"(getter),不会生成setter方法
注意
readwrite是默认属性
对readonly修饰的属性进行写操作会报错,但可以直接访问成员变量
引用计数
assign
assign是默认关键字,用来修饰基本数据类型
assign修饰对象类型,不会改变对象的引用计数
assign的对象会产生悬垂指针
,当对象没有持有者被废弃时,assign修饰的对象指针还是会指向原来的地址,会产生悬垂指针。访问悬垂指针,可能会导致程序crash
unsafe_unretained
此特质的语义与assign相同,但它适用于“对象类型”,该特质表达了一种“非持有关系
”(不保留,unretained),当目标对象遭到摧毁时,属性值不会自动清空
,这一点与weak有区别。
weak
弱引用,表示该属性是一种“非拥有关系
”。为这种属性设置新值时既不会保留新值也不会释放旧值,类似于assgin。 然而在对象被摧毁时,属性也会被清空(置nil)
__weak对性能会有一定的消耗,使用__weak,需要检查对象是否被释放,在追踪是否被释放的时候当然需要追踪一些信息,那么此时__unsafe_unretained比__weak快,而且一个对象有大量的__weak引用对象的时候,当对象被废弃,那么此时就要遍历weak表,把表里所有的指针置空,消耗cpu资源。
strong
strong 是对象的默认属性关键字,此特质表明该属性定义了一种“持有关系
”,为这种属性设置新值时,设置方法既会先保留(retain)新值,并释放(release)旧值,然后再将新值设置上去
copy
此特质所表达的所属关系与strong类似,然后设置方法并不保留(retain)新值,而是将其复制(copy)
copy修饰不可变对象,strong修饰可变对象
copy修饰不可变对象、原对象为不可变对象时,将原对象赋值给属性,会将原对象进行copy,此时是浅复制,两个指针指向的是同一个地址。
copy修饰不可变对象,原对象为可变对象时,将原对象赋值给属性,会将原对象进行copy,此时是深复制,两个对象指向的不同的地址,属性所指的是可变对象的副本,原对象如果被修改的话,不会影响属性的值
copy修饰可变对象,如果原对象是不可变对象并赋值给属性,那么进行的是浅复制,指向同一地址
copy修饰可变对象,如果原对象是可变对象并赋值给属性,此时进行的是深复制,属性指向的是原对象的不可变副本,即此时属性为不可变对象,这时对属性进行增删改的操作,就会因为找不到方法而报错。
如果不可变对象用strong修饰的话,又会有什么结果呢
由于strong修饰属性在设置新值时,在setter方法中保留新值、并释放旧值,将新值设置上去,此时与原对象指向的是同一地址。
当原对象为可变对象时,将原对象赋给strong修饰的不可变对象,修改原对象,那我们不可变的对象也会随之改变
retain
retain在MRC下使用效果与strong一致,在ARC环境下使用较少
系统默认关键字
基本数据
:atomic,readweite,assignOC对象
:atomic, readwrite,strong
Q&A
深浅拷贝区别?
-
浅拷贝
对内存地址复制, 让目标对象指针和源对象指向同一片内存空间
增加对象引用计数不会发生新内存分配 -
深拷贝
让目标对象指针和源对象指针, 指向2片内容相同的内存空间
不增加对象引用计数
产生新的内存分配。
2片的内存空间内容相同, 但不是同一个
一些关键字的比较?
-
strong和weak的区别
strong修饰的属性表示对对象的强引用,所指向的对象引用计数+1。
weak修饰的属性表示对对象的弱引用,所指向的对象的引用计数不会增加,当所指的对象销毁时,weak修饰的属性会置nil -
assign和weak的区别
assign修饰基本数据类型,也能修饰对象。修饰对象类型时并不会改变对象的引用计数,当所修饰的对象被废弃时,对象指针还指向原来的地址,会产生悬垂指针,再次访问程序可能会crash
weak只修饰对象类型,不能修饰基本数据类型。修饰对象类型时不会改变对象的引用计数,当所修饰的对象被废弃时,对象指针会自动置nil。此时访问对象指针或像对象指针发送消息并不会引起程序崩溃 -
unsafe_unretained和weak的区别
unsafe_unretained和assign差不多,修饰对象类型不会改变对象的引用计数,对象被废弃时,对象指针仍指的是原地址,产生悬垂指针,再次访问可能会出错。
weak修饰对象类型,不改变对象的引用计数,对象被销毁时,被weak修饰的对象指针会置nil。
__weak对性能有一定的消耗,使用__weak,需要检查对象是否被释放,在追踪是否被释放的时候肯定会追踪一些信息,此时unsafe_unretained比weak快,而当一个对象有大量的weak引用对象时,当对象被废弃时,那么此时就要遍历weak表,将表里的指针都置nil,消耗cpu资源。
当我们很明确对象的生命周期时,可以使用unsafe_unretained代替weak,可以稍微提高一点性能,但其实这点性能确实微乎其微 -
strong和copy
strong修饰可变对象类型。strong属性关键字会在setter方法中(先判断对象是否与参数相同)对新值进行retain,对旧值release,再将新值赋给对象指针。(ARC下retain,strong基本相同)
copy修饰不可变对象类型,保证不可变对象类型指针指向的一定是不可变对象。copy属性关键字会在setter方法中(先判断对象是否与参数相同),对旧值release,将新值copy后赋给属性
如果用copy修饰NSMutableArray会出现什么问题?
无论原对象是可变类型还是不可变类型,赋值给copy修饰的NSMutableArray对象都会先进行copy,随后将一份不可变对象赋值给NSMutableArray对象,此时如果对NSMutableArray进行增、删、改就会因找不到方法导致程序崩溃