編寫高質量iOS與OS X程式碼的52個有效方法(二)

眯大帥發表於2018-01-25

##物件、訊息、執行時

###理解“屬性”的概念 屬性的基礎用法就不多敘述了 屬性特質

@property (nonatomic,readwrite,copy) NSString *name;
複製程式碼

屬性擁有的特質分為4類 ####1、原子性 atomic                 設定成員變數的@property屬性時,預設為atomic,提供多執行緒安全。                 在多執行緒環境下,原子操作是必要的,否則有可能引起錯誤的結果。加了atomic,setter函式會變成下面這樣:

{lock} 
  if (property != newValue) {                              
    [property release];                                       
    property = [newValue retain];                               
  }                      
{unlock}
複製程式碼

nonatomic         禁止多執行緒,變數保護,提高效能。        atomic是Objc使用的一種執行緒保護技術,基本上來講,是防止在寫未完成的時候被另外一個執行緒讀取,造成資料錯誤。而這種機制是耗費系統資源的,所以在iPhone這種小型裝置上,如果沒有使用多執行緒間的通訊程式設計,那麼nonatomic是一個非常好的選擇。         指出訪問器不是原子操作,而預設地,訪問器是原子操作。這也就是說,在多執行緒環境下,解析的訪問器提供一個對屬性的安全訪問,從獲取器得到的返回值或者通過設定器設定的值可以一次完成,即便是別的執行緒也正在對其進行訪問。如果你不指定 nonatomic ,在自己管理記憶體的環境中,解析的訪問器保留並自動釋放返回的值,如果指定了 nonatomic ,那麼訪問器只是簡單地返回這個值。 開發iOS程式時,應該使用nonatomic屬性,因為atomic屬性會嚴重影響效能。 ####2、讀/寫許可權 readwrite會自動生成getter/setter方法 readonly只有getter方法 ####3、記憶體管理語義 assign “設定方法”只會執行鍼對“純量型別”(CGFloat&&NSIntger等)的簡單賦值操作。 strong 此特質表明該屬性定義了一種“擁有關係”。賦值時會保留新值,並釋放舊值,然後再講新值設定上去 weak 此特質表明該屬性定義了一種“非擁有關係”。賦值時既不保留新值,也不釋放舊值,同assign相似。然而屬性所指的物件銷燬時,屬性值也會被nil; unsafe_unretained此特質的語義和assign相同,但他適用於物件型別,該特質表達一種“非擁有關係”(“不保留”),當目標物件遭到銷燬時,屬性值不會被清空,與weak有區別。 copy所表達的所屬關係與strong相似。然而設定方法並不保留新值,而是將其copy。當屬性型別為NSString時,經常用此特質來保護其封裝性,因為傳遞給設定方法的新值有可能指向一個NSMutableString類的例項。這個類是NSString的子類,表示可修改字串,此時若是不拷貝字串,那麼設定完屬性之後,字串的值可能會在物件不知情的情況下遭到更改。

weak比 assign多了一個功能就是當屬性所指向的物件消失的時候(也就是記憶體引用計數為0)會自動賦值為 nil,這樣再向 weak修飾的屬性傳送訊息就不會導致野指標操作crash。

weak如何自動置nil? runtime 對註冊的類, 會進行佈局,對於 weak 物件會放入一個 hash 表中。 用 weak 指向的物件記憶體地址作為 key,當此物件的引用計數為0的時候會 dealloc,假如 weak 指向的物件記憶體地址是a,那麼就會以a為鍵, 在這個 weak 表中搜尋,找到所有以a為鍵的 weak 物件,從而設定為 nil。

####用@property宣告的NSString(或NSArray,NSDictionary)經常使用copy關鍵字,為什麼?如果改用strong關鍵字,可能造成什麼問題?

因為父類指標可以指向子類物件,使用 copy 的目的是為了讓本物件的屬性不受外界影響,使用 copy 無論給我傳入是一個可變物件還是不可物件,我本身持有的就是一個不可變的副本. 如果我們使用是 strong ,那麼這個屬性就有可能指向一個可變物件,如果這個可變物件在外部被修改了,那麼會影響該屬性.

copy 此特質所表達的所屬關係與 strong 類似。然而設定方法並不保留新值,而是將其“拷貝” (copy)。 當屬性型別為 NSString 時,經常用此特質來保護其封裝性,因為傳遞給設定方法的新值有可能指向一個 NSMutableString 類的例項。這個類是 NSString 的子類,表示一種可修改其值的字串,此時若是不拷貝字串,那麼設定完屬性之後,字串的值就可能會在物件不知情的情況下遭人更改。所以,這時就要拷貝一份“不可變” (immutable)的字串,確保物件中的字串值不會無意間變動。只要實現屬性所用的物件是“可變的” (mutable),就應該在設定新屬性值時拷貝一份。

###在物件內部儘量直接訪問例項變數

1、訪問例項變數不經過objc的方法派發(method dispatch),所以速度更快。在這種情況下,編譯器所生成的程式碼會直接訪問儲存物件例項變數的那塊記憶體。 2、直接訪問例項變數,不會呼叫getter/setter方法 如果需要使用KVO或懶載入,還是得使用屬性 在物件內部讀取資料時,應該直接通過例項變數來讀,而寫入資料時,應該通過屬性來寫。 在初始化方法及dealloc方法中,總是應該直接通過例項變數來讀寫資料。 有時會使用惰性初始化技術配置某份資料,這種情況下,需要通過屬性來讀取資料。 ###理解“物件等同性” NSObject協議中有兩個用於判斷等同性的關鍵方法:

-(Bool)isEqual:(id)object;
-(NSUInteger)hash;
複製程式碼

若想檢測物件的等同性,請提供“isEqual:”與hash方法。 相同的物件必須具有相同的hash碼,但是兩個hash碼相同的物件卻未必相同。 不要盲目的逐個監測每條屬性,而是應該依照具體需求來制定檢測方案。 編寫hash方法時,應該使用計算速度快而且雜湊碼碰撞機率低的演算法。

###理解objc_msgSend的作用

id returnValue = [someObject messageName:parameter];
複製程式碼

someObject叫做receiver,messageName叫做selector,selector與引數合起來稱為message。編譯器看到訊息後,將其轉換為一條標準的C語言函式呼叫,如下

id returnValue = objc_msgSend(someObject,@selector(messageName:) parameter);
複製程式碼

訊息由receiver,selector及引數構成。給某物件“傳送訊息”也就相當於在該物件上呼叫方法。 發給某物件的全部訊息都要由“動態訊息派發系統”來處理,該系統會查出對應的方法,並執行其程式碼。

###理解訊息轉發機制 什麼是訊息轉發?當物件接收到無法解讀的訊息後,就會啟動訊息轉發機制。

訊息轉發分為兩個階段。第一階段先徵詢接收者所屬的類,看其是否能動態新增方法,已處理當前這個“未知的selector”,這叫做“動態方法解析”。第二階段涉及“完整的訊息轉發機制”。如果執行期系統已經把第一階段執行完了,那麼接收者自己就無法再以動態新增方法的手段來響應包含該selector的訊息了。此時執行期系統就會請求接收者以其他手段來處理與訊息相關的方法呼叫。細分為兩步:首先,讓接收者看看有沒有其他物件能處理這條訊息。如果有,則執行期系統會把訊息轉給那個接收者,於是訊息轉發結束。如果沒有這個“備援接收者”,則啟動完整的訊息轉發機制,執行期系統會把與訊息有關的全部細節封裝到NSInvocation物件中,再給接收者最後一次機會,令其設法解決當前還未處理的這條訊息。

訊息轉發機制流程圖
若物件無法響應某個selector,則進入訊息轉發流程。 通過執行期的動態方法解析功能,我們可以在需要用到某個方法時再將其加入類中。 物件可以將其無法解讀的某些selector轉交給其他物件處理。 經過上述兩步後,如果還是沒辦法處理selector,那就啟動完整的訊息轉發機制。

###用method swizzling除錯黑盒方法 在runtime中,可以向類中新增或替換selector所對應的方法實現。 使用另一份實現來替換原有的方法實現,這道工序叫做method swizzling,開發者常用此技術向原有視線中新增功能。 一般來說,只有除錯程式的時候才需要在runtime中修改方法實現,這種做法不宜濫用。

相關文章