重識iOS之Property
筆者最近梳理 iOS 知識脈絡,計劃寫一個名為“ 重識iOS ”的系列,內容來自平時的學習筆記,參考了一些文章和書籍,融入自己的理解以記錄。歡迎交流指正。
系列篇:
- ofollow,noindex">重識iOS之Category

property
Property的介紹
- 簡介:屬性( property )是 Objective-C 的一項特性,用於 封裝物件中的資料 。這一特性可以令編譯器自動編寫與屬性相關的存取方法,並且儲存為各種例項變數。
- 本質:屬性的本質是例項變數與存取方法的結合。
@property = ivar + getter + setter
Property的特質
- 原子性: atomic / nonatomic
- 讀寫許可權: readwrite / readonly
- 記憶體管理語義: assign / strong / copy / weak / unsafe_unretained
- 方法名: getter=<name> / setter=<name>
atomic 與 nonatomic
問題: 什麼是原子性? 說明並比較atomic和nonatomic。 atomic是百分之百安全的嗎?
- 原子性 :併發程式設計中確保其操作具備整體性,系統其它部分無法觀察到中間步驟,只能看到操作前後的結果。
- atomic :原子性的,編譯器會通過鎖定機制確保 setter 和 getter 的完整性。
- nonatomic :非原子性的,不保證 setter 和 getter 的完整性。
- 區別 :由於要保證操作完整, atomic 速度比較慢,執行緒相對安全; nonatomic 速度比較快,但是執行緒不安全。 atomic 也不是絕對的執行緒安全,當多個執行緒同時呼叫 set 和 get 時,就會導致獲取的值不一樣。由於鎖定機制開銷較大,一般 iOS開 發中會使用 nonatomic ,而 macOS 中使用 atomic 通常不會有效能瓶頸。
- 拓展 :要想執行緒絕對安全,就要使用 @synchronized 同步鎖。但是由於同步鎖有等待操作,會降低程式碼效率。為了兼顧執行緒安全和提升效率,可採用 GCD併發佇列 進行優化改進。 get 使用同步派發, set 使用非同步柵欄。
//同步鎖 - (NSString *)someString { @synchronized(self) { return _someString; } } - (void)setSomeString:(NSString *)someString { @synchronized(self) { _someString = someString; } } //併發佇列 _queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); - (NSString *)someString { __block NSString *localSomeString; dispatch_sync(_queue, ^{ localSomeString = _someString; }); return localSomeString; } - (void)setSomeString:(NSString *)someString { dispatch_barrier_async(_queue, ^{ _someString = someString; }); }
readwrite 與 readonly
讀寫許可權不寫時預設為 readwrite 。一般可在 .h 裡寫成 readonly ,只對外提供讀取,在 .m 的 Extension 中再設定為 readwrite 可進行寫入。
//.h檔案 #import <Foundation/Foundation.h> @interface MyClass : NSObject @property (nonatomic, readonly, copy) NSString *name; @end //.m檔案 #import "MyClass.h" @interface MyClass() @property (nonatomic, readwrite, copy) NSString *name; @end
記憶體管理語義
1.關鍵詞
- strong :表示指向並擁有該物件。其修飾的物件引用計數會 +1 ,該物件只要引用計數不為 0 就不會銷燬,強行置空可以銷燬它。一般用於修飾物件型別、字串和集合類的可變版本。
- copy :與 strong 類似,設定方法會拷貝一份副本。一般用於修飾字串和集合類的不可變版, block 用 copy 修飾。
- weak :表示指向但不擁有該物件。其修飾的物件引用計數不會增加,屬性所指的物件遭到摧毀時屬性值會清空。ARC環境下一般用於修飾可能會引起迴圈引用的物件, delegate 用 weak 修飾, xib 控制元件也用 weak 修飾。
- assign :主要用於修飾基本資料型別,如 NSIteger 、 CGFloat 等,這些數值主要存在於棧中。
- unsafe_unretained :與weak類似,但是銷燬時不自動清空,容易形成野指標。
2.比較 copy 與 strong
- copy 與 strong :相同之處是用於修飾表示擁有關係的物件。不同之處是 strong 複製是多個指標指向同一個地址,而 copy 的複製是每次會在記憶體中複製一份物件,指標指向不同的地址。 NSString 、 NSArray 、 NSDictionary 等不可變物件用 copy 修飾,因為有可能傳入一個可變的版本,此時能保證屬性值不會受外界影響。
- 注意 :若用 strong 修飾 NSArray ,當陣列接收一個可變陣列,可變陣列若發生變化,被修飾的屬性陣列也會發生變化,也就是說屬性值容易被篡改;若用 copy 修飾 NSMutableArray ,當試圖修改屬性數組裡的值時,程式會崩潰,因為陣列被複製成了一個不可變的版本。
3.比較 assign、weak、unsafe_unretain
- 相同點 :都不是強引用。
- 不同點 : weak 引用的 OC 物件被銷燬時, 指標會被自動清空,不再指向銷燬的物件,不會產生野指標錯誤; unsafe_unretain 引用的 OC 物件被銷燬時, 指標並不會被自動清空, 依然指向銷燬的物件,很容易產生野指標錯誤:
EXC_BAD_ACCESS
; assign 修飾基本資料型別,記憶體在棧上由系統自動回收。
getter=<name> 與 setter=<name>
<>
中為方法名,通過此特質來指定存取方法的名稱。
//.h檔案 @interface MyClass : NSObject @property (nonatomic, assign, getter=isOn) BOOL on; @end //.m檔案 @implementation MyClass - (BOOL)isOn { return self.on; } @end
Property的預設設定
- 基本資料型別: atomic, readwrite, assign
- 物件型別: atomic, readwrite, strong
- 注意 :考慮到程式碼可讀性以及日常程式碼修改頻率,規範的編碼風格中關鍵詞的順序是:原子性、讀寫許可權、記憶體管理語義、getter/getter。
延伸
我們已經知道 @property 會使編譯器自動編寫訪問這些屬性所需的方法,此過程在編譯期完成,稱為 自動合成 ( autosynthesis )。與此相關的還有兩個關鍵詞: @dynamic 和 @synthesize 。
- @dynamic :告訴編譯器不要自動建立實現屬性所用的例項變數,也不要為其建立存取方法。即使編譯器發現沒有定義存取方法也不會報錯,執行期會導致崩潰。
- @synthesize :在類的實現檔案裡可以通過 @synthesize 指定例項變數的名稱。
- 注意 :在 Xcode4.4 之前, @property 配合 @synthesize使用 , @property 負責宣告屬性, @synthesize 負責讓編譯器生成 帶下劃線的例項變數並且自動生成 setter、getter 方法。 Xcode4.4 之後 @property 得到增強,直接一併替代了 @synthesize 的工作。
參考: