iOS 属性 @property 详细探究
@property 的本质其实是:ivar (实例变量) + getter + setter
存取器方法
getter=getterName
setter=setterName
读写权限
readwrite:表示自动生成对应的 getter 和 setter 方法,即可读可写权限, readwrite是编译器的默认选项。
readonly:表示只生成 getter ,不需要生成 setter ,即只可读,不可以修改。
内存管理
strong // 强引用,引用计数+1
assign // assign是指针赋值,不对引用计数操作,对象销毁后不会自动置为nil
copy // copy出一个新对象,引用计数为1
weak // 弱引用,不对引用计数操作,对象销毁时自动置为nil
retain // 强引用,对象引用计数+1
unsafe_unretained // 弱引用,不对引用计数操作,对象销毁时不会自动置为nil
strong
表示强引用关系,即修饰对象的引用计数会+1,通常用来修饰对象类型,可变集合及可变字符串类型。当对象引用计数为0,即不被任何对象持有,且此对象不再显示在列表中时,对象就会从内存中释放。
assign
对象不进行 retain 操作,即不改变对象引用计数。通常用来修饰基本数据类型( NSInteger, CGFloat, Bool, NSTimeInterval 等),内存在栈上由系统自动回收。
也可以用来修饰 NSObject 类型对象,因为 assign 不会改变修饰对象的引用计数,所以当修饰对象的引用计数为0,对象销毁的时候,对象指针不会被自动清空。而此时对象指针指向的地址已被销毁,这时再访问该属性会产生野指针错误:EXC_BAD_ACCESS,因此 assign 通常用来修饰基本数据类型。
copy
当调用修饰对象的 setter 方法时,会建立一个引用计数为 1 的新对象,即对象会在内存里拷贝一份副本,两个指针指向不同的内存地址。一般用于修饰字符串( NSString )和集合类( NSArray , NSDictionary )的不可变变量,Block 也是用 copy 修饰。
weak
表示弱引用关系,修饰对象的引用计数不会增加,当修饰对象被销毁的时候,对象指针会自动置为 nil,防止出现野指针。weak 也用来修饰 delegate ,避免循环引用。另外 weak 只能用来修饰对象类型,且是在 ARC 下新引入的修饰词,MRC 下相当于使用 assign 。
weak 的底层实现是基于 Runtime 底层维护的 SideTables 的 hash 数组,里面存储的是一个 SideTable 的数据结构。
retain
retain是在 MRC 下常用的修饰词:
ARC 下已不再使用 retain ,而是使用 strong 代替。retain 同 strong 类似,用来修饰对象类型,强引用对象,其修饰对象的引用计数会 +1,不会对对象分配新的内存空间。
unsafe_unretained
unsafe_unretained同 weak 类似:
unsafe_unretained 不会对对象的引用计数 +1,只能用来修饰对象类型,修饰的对象在被销毁时,其指针不会自动清空,指向的仍然是已销毁的对象,这时再调用该指针会产生野指针 :EXC_BAD_ACCESS 错误。
原子性
atomic 原子性
系统会自动给生成的 getter/setter 方法进行加锁操作;
nonatomic 非原子性
系统不会给自动生成的 getter/setter 方法进行加锁操作;
由此可见,对属性对象的加锁操作仅限于对象的 getter/setter 操作,如果是 getter/setter 以外的操作,该加锁并没有意义。因此 atomic 的原子性,仅能保障对象的 getter/setter 的线程安全,并不能保障多线程下对对象的其他操作安全。如一个线程在 getter/setter 操作,另一个线程进行 release 操作,可能会导致 crash。此种场景的线程安全,还需要由开发者自己进行处理。