在上一篇runtime中我們了解到了對象, 類的結(jié)構體定義,了解到元類的定義, 對象,類結(jié)構體中isa指針的指向等,這篇我們將進入實戰(zhàn),該篇對于在編碼過程中有很大的幫助,減少項目中的代碼量黎侈。
1.關聯(lián)對象(Associated Object)
關聯(lián)對象類適用于動態(tài)添加屬性, 我們利用OC的運行時特性,在分類中添加一個屬性.我們可以把關聯(lián)對象想象成一個OC的對象(如字典), 這個對象通過給定的key連接到這個類的實例上, 由于使用的是C接口, 所以key是一個void指針(const void *). 我們還需要指定的一個內(nèi)存管理策略, 用來確定如何管理這個對象的內(nèi)存, 管理內(nèi)存的策略, 我相信很多人都見過下面的枚舉
當宿主對象被釋放時,會根據(jù)指定的內(nèi)存管理策略來處理管理對象. 如果指定的策略是assgin, 則宿主釋放的時候, 關聯(lián)對象不會被釋放; 如果指定是retain或者copy, 當宿主對象釋放時候, 關聯(lián)對象也會被釋放. 當然我們也可以考慮線程安全這個因素
首先,我們來看個例子, 如果我們創(chuàng)建一個UIButton對象, 會使用addTarget:這個方法添加action, 但是這么寫太過于繁瑣, 我們可以新建一個UIButton的分類, 給定一個block屬性, 在該分類的.m文件中重寫該屬性的setter和getter方法, 首先setter方法中添加關聯(lián)對象, 如果該對象已經(jīng)有SEL方法, 先移除, 然后給self添加方法; 之后再getter方法中利用objc_getAssociatedObject(self, cvenies_btnActionKey)獲取該屬性,在利用Target...Action添加的方法中使用block, 則可以完成block回調(diào)需要處理的方法, 此后, 再創(chuàng)建UIButton對象之后, 可以直接利用buttonObject.blockBtnAction = ^(UIButton *sender) {}這個方法處理點擊方法了.當然, 這只是runtime添加屬性簡單的實現(xiàn), 也可以動態(tài)添加手勢等屬性, 根據(jù)自己的需要構建不同的分類.
2.Method Swizzling
首先,我們來了解一下Method Swizzling的定義, 所謂Method swizzling指的是改變一個已存在的選擇器對應的實現(xiàn)的過程,它依賴于Objectvie-C中方法的調(diào)用能夠在運行時進改變——通過改變類的調(diào)度表(dispatch table)中選擇器到最終函數(shù)間的映射關系闷游。
同樣, 我們通過一個例子來引入Method Swizzling的用處, 對于一個大型的項目, 會有很多方向的部門并行開發(fā), 而且很多大公司都在堅持敏捷開發(fā)的原則, 對于更新迭代很快的項目, 會存在很多的控制器, 當出現(xiàn)bug的時候, 可能會難以尋找代碼所在的控制器, 這時候我們需要追蹤定位頁面所在的控制器, 會在viewWillAppear方法中添加 NSLog(@"當前控制器類名稱:%@", [[self class] description]); 這樣一段代碼用于打印當前控制器的名字, 當然會用以下3種方法可供參考, 我們來逐條分析:
(1)最笨的方法, 在每個控制器的viewWillAppear中都添加 NSLog(@"當前控制器類名稱:%@", [[self class] description]); 這樣一段代碼, 控制器中代碼量會不斷加重, 所以這是種最垃圾的處理方法;
(2)創(chuàng)建一個BaseViewController, 在BaseViewController的viewWillAppear方法中添加 NSLog(@"當前控制器類名稱:%@", [[self class] description]); 這樣一段代碼, 所有項目中的控制器都繼承自BaseViewController, 這是一種相對不錯的方法
(3)最后我們來嘗試一下Method Swizzling
使用Method Swizzling需要注意幾點:
<1>Swizzling應該在+load方法中實現(xiàn)峻汉。
每個類的這兩個方法會被Objective-C運行時系統(tǒng)自動調(diào)用贴汪,+load是在一個類最開始加載時調(diào)用,+initialize是在應用中第一次調(diào)用該類或它的實例的方式之前調(diào)用休吠。這兩個方法都是可選的扳埂,只有實現(xiàn)了才會被執(zhí)行。
因為method swizzling會影響全局瘤礁,所以減少冒險情況就很重要阳懂。+load能夠保證在類初始化的時候就會被加載,這為改變系統(tǒng)行為提供了一些統(tǒng)一性柜思。但+initialize并不能保證在什么時候被調(diào)用——事實上也有可能永遠也不會被調(diào)用岩调,例如應用程序從未直接的給該類發(fā)送消息。
<2>使用dispatch_once方法, 是為了保證該控制器只被創(chuàng)建一次.
使用Method Swizzling相對于上面的兩種給人更加裝逼的感覺, 當然我們知道Method Swizzling的具體實現(xiàn)才是最重要的.
關于Objective-C中runtime還有更多的黑魔法值得我們?nèi)ヌ剿? 一定要好好的研究底層的實現(xiàn), 才是我們掌握一門語言的關鍵...
代碼參考:https://github.com/CveniEs/CveniEsSchema.git
文章參考:南峰子的技術博客
CocoaChina/Method Swizzling專題