@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”。