知識點:可變數組的屬性使用copy修飾的後果

問題

視頻What's New in LLVM 中,從12:05的時間開始有個關於NSMutableArray可變數組屬性的使用問題。objective-c

運行後報錯圖以下:

分析

self.photos的實際類型是 __NSMutable0,也就NSArray類型。沒有addObject的方法。數組

進一步探討

  1. OC是門動態型語言,在編譯階段不會作類型檢測。OC的內存管理是引用計數,在ARC環境下,屬性@property的內存管理語義關鍵字有copy,weak,strong,asssin。在編譯階段,默認狀況下編譯器會生成一個成員變量、一個setter方法、一個getter方法。而在setter方法中,會根據內存管理語義作相應的引用計數相關的操做。當使用copy修飾屬性時,在setter中實際操做是拷貝了一份不可變的類型對象。這樣的話,即便是其是可變類型,在被賦值後,咱們獲得的是倒是不可變類型的對象。安全

  2. OC具備多態性,父類能夠指向子類。對象最終類型會在運行期根據實例化對象確認。在運行時階段其isa指向的是[NSArray Class]。那麼當向self.photos發送一個addObject消息時,self.photos對象是接收不到這個消息的。由於addObject是NSArray的子類NSMutbleArray的方法。app

  3. 屬性語義多種:ide

  • 原子性(Atomicity):原子性(atomic)、非原子性(nonatomic)
  • Setter語義(Setter Semantics):strong,weak,copy,asssin
  • 讀寫屬性(Writability): readwrite/readonly 原子性是具備線程安全的,會在屬性的setter方法內部加個一個自旋鎖、而非原子性是不會在setter方法中加鎖的,是非線程安全的。在小型設備的上,內存空間是有限的。給屬性加自旋鎖是很是消耗資源的。而且不必定說使用了原子性就能保證該屬性線程安全。這個僅僅是在setter方法中是安全的,這也是atomic該作的事。若是繞開setter方法使用其餘的方式給屬性賦值,依然是不安全的,好比使用KVC。
  1. ARC下,屬性的默認語義是:
  • 基本數據:atomic、assgin、readwrite
  • 普通的OC對象:atomic、strong、readwrite

在此狀況下,實際編譯器添加setter方法以下:性能

// ARC
- (void)setPhotos:(NSMutableArray<UIImage *> *)photos{
    // 1. 開始加鎖,非天然語言,這裏不寫代碼了
    _photos = [photos copy];
    // 2. 加鎖結束
}
複製代碼

那麼獲得的是個self.photos實際是NSArray類。atom

從上就發現了2個問題:屬性就是使用了關鍵字atomic、copy修飾。那麼這裏會加鎖而且獲得NSArray類的self.photos。spa

相關概念:線程

  1. 自旋鎖:當上一個線程的任務沒有執行完畢的時候(被鎖住),那麼下一個線程會一直等待(不會睡眠),當上一個線程的任務執行完畢,下一個線程會當即執行。
  2. 自旋鎖應用場景: 比較適合作一些不耗時的操做

解決

一、修改copy語義在setter中默認內容:code

方式一: 手動重寫setter方法,使用賦值前mutableCopy。以下,這樣獲取到的就是NSMutableArray類型的對象。

- (void)setPhotos:(NSMutableArray<UIImage *> *)photos{
    _photos = [photos mutableCopy];
}
複製代碼

方式二: 使用關鍵字strong修飾屬性。咱們獲得的依然是可變類型。

- (void)setPhotos:(NSMutableArray<UIImage *> *)photos{
    _photos = photos;
}
複製代碼

二、原子性修改: 使用:nonatomic,減小小型設備中性能消耗。

相關文章
相關標籤/搜索