@property 有两个的作用:一是自动生成私有属性(一般是下划线+属性名),而是自动生成 getter
和 setter
方法。
声明属性时,紧跟在 @property 之后的属性修饰符则描述了自动生成 getter
和 setter
方法的规则。
根据MRC和ARC划分属性修饰符的使用范围:
MRC:nonatomic,atomic,retain,assign,copy,readwrite,readonly
ARC:nonatomic,atomic,strong,weak,assign,copy,readwrite,readonly
-
nonatomic
- nonatomic 表示非原子属性
- 并发访问性能高,但是访问不安全
- 它直接访问内存中的地址,不关心其他线程是否在改变这个值,并且中间没有死锁保护;所以可能拿不到完整的值
- 并发访问性能高,但是访问不安全
- nonatomic 表示非原子属性
-
atomic
- atomic 表示原子属性
- 系统生成的 getter/setter 会保证 get、set 操作的完整性,不受其他线程影响
- 系统生成的 getter/setter 方法中,使用了 @synchronized(self)
- 如果一个线程正在执行 getter/setter,其他线程就得等待
- 如果有另一个线程同时在调 [property release],那可能就会crash,因为 release 不受 getter/setter 操作的限制。
- 也就是说,atomic 修饰的属性只能说是读/写安全的,但并不是线程安全的
- 因为别的线程还能进行读写之外的其他操作。
- 线程安全需要开发者自己来保证。
- 也就是说,atomic 修饰的属性只能说是读/写安全的,但并不是线程安全的
- 系统生成的 getter/setter 会保证 get、set 操作的完整性,不受其他线程影响
- 系统默认的属性是 atomic
- atomic 表示原子属性
-
strong
- strong 表示对对象的强引用
- 强引用时,引用计数会 +1
- 给 strong 属性赋值时,setter 方法中会先 release 旧值再 retain 新值并赋值
- 两个对象之间相互强引用会造成循环引用,内存泄漏
- strong 表示对对象的强引用
-
weak
- weak 表示对象的弱引用
弱引用时,不会使传入的对象计数+1
-
被其修饰的对象随时可能被系统销毁回收
- 当该对象的引用计数为 0,则会被回收,对象被释放以后,weak 指针会被自动设置为 nil
在OC的运行时环境中,维护了一种 weak 表(哈希表)
这张哈希表用对象的首地址作为 key,用 weak 指针自身的地址组成的数组作为 value
当对象被释放后,通过这个对象的起始地址来找到所有指向它的 weak 指针,并将它们指向nil
- 当该对象的引用计数为 0,则会被回收,对象被释放以后,weak 指针会被自动设置为 nil
weak 多用于避免循环引用
- weak 表示对象的弱引用
-
assign
- assign 主要用于修饰基本数据类型
- 包括OC基本数据类型(NSInteger,CGFloat)和C数据类型(int, float, double, char)
- 基本数据类型存储在栈中,内存不用程序员管理。
- assign 也可以修饰对象,但是当对象被释放后,指针依然指向之前的内存地址。
- 此时,访问被释放的地址就会 crash- 这个已经被释放了的对象被称为 僵尸对象
- assign 在 MRC 和 ARC 下都可以使用
- assign 主要用于修饰基本数据类型
-
retain
- 在 MRC 下使用,用于修饰对象
- 被 retain 修饰的对象,retainCount要+1
- retain 只能修饰 OC 对象,不能修饰非 OC 对象(如 Core Foundation 对象)
- retain 会增加对象的引用计数,而基本数据类型或者 Core Foundation 对象都没有引用计数
- 赋值时,先 release 旧值,再 retain 新值
-
copy
- copy 表示在赋值时使用传入值的一份拷贝
- 赋值时,创建了一个新的对象,并将传入对象的值全部拷贝到新对象
- 赋值后,新对象的 retainCount 为 1,而旧对象的 retainCount 不变
- 只能修饰对象类型,不能修饰基础数据类型
- 用copy修饰的对象,必须实现
NSCopying 协议,也就是实现方法-(id)copyWithZone:(nullable NSZone *)zone
- 系统自动生成的 setter 方法中会调用这个方法
- 至于 copy 是深拷贝还是浅拷贝完全是看 copyWithZone 的实现方式,copy 修饰符和深拷贝、浅拷贝没有关系
- 一般用于修饰不可变的属性(NSArray,NSDictionary,NSString,block)
- 即使用 copy 修饰 NSMutableArray,将一个可变 NSMutableArray 赋值给 copy 修饰的属性也会变成不可变数组 NSArray
- 若 a 被 copy 修饰,则
a = b
等价于a = [b copy]
。
- 若 a 被 copy 修饰,则
- 为什么要用 copy 修饰 NSString ?
如果用 retain 修饰 NSString,
当把 NSMutableString 赋值给 NSString 时,只是拷贝了指针;
如果赋值后源字符串改变,这个属性值也跟着改变。(不可控)
如果用 copy 修饰 NSString,
当把 NSMutableString 赋值给 NSString 时,会生成一份拷贝内容;
即使赋值后源字符串改变,这个属性值也不会改变。(保证了安全)
- 即使用 copy 修饰 NSMutableArray,将一个可变 NSMutableArray 赋值给 copy 修饰的属性也会变成不可变数组 NSArray
- copy 表示在赋值时使用传入值的一份拷贝
-
readwrite
- readwrite 表示该属性可读可写
- 系统会自动创建 setter 和 getter 方法
- readwrite 是默认的属性修饰符
-
readonly
- readonly 表示该属性只可读,不可写
- 只会生成get方法
- 对用 readonly 修饰的属性赋值时,编译器会报错提示:“Assignment to readonly property”。