iOS開發之Runtime常用樣本總結,iosruntime樣本

來源:互聯網
上載者:User

iOS開發之Runtime常用樣本總結,iosruntime樣本

經常有小夥伴私下在Q上問一些關於Runtime的東西,問我有沒有Runtime的相關部落格,之前還真沒正兒八經的總結過。之前只是在解析第三方架構源碼時,聊過一些用法,也就是這些第三方架構中用到的Runtime。比如屬性關聯,動態擷取屬性等等。本篇部落格就針對Runtime這個主題來總結一些其常用的一些方法,當然“空談誤國”,今天部落格中所聊的Runtime依然要依託於本篇部落格所涉及的Demo。

本篇部落格所聊的Runtime的內容大概有:動態擷取類名、動態擷取類的成員變數、動態擷取類的屬性列表、動態擷取類的方法列表、動態擷取類所遵循的協議列表、動態添加新的方法、類的執行個體方法實現的交換、動態屬性關聯、訊息發送與訊息轉寄機制等。當然,本篇部落格總結的是運行時常用的功能,並不是所有Runtime的內容。

 

一、構建Runtime測試案例

本篇部落格的內容是依託於執行個體的,所以我們在本篇部落格中先構建我們的測試類別,Runtime將會對該類進行相關的操作。下方就是本篇部落格所涉及Demo的目錄,上面的RuntimeKit類是講Runtime常用的功能進行了簡單的封裝,而下方的TestClass以及相關的類目就是我們Runtime要操作的對象了。下方會對TestClass以及類目中的內容進行詳細介紹。

  

 

下方這幾個就是我們的測試類別TestClass的主要部分,因為TestClass是專門用來測試的類,所以其涉及的內容要盡量的全面。TestClass遵循了NSCoding, NSCopying這兩個協議,並且為其添加了公有屬性、私人屬性、私人成員變數、 公有執行個體方法、私人執行個體方法、類方法等。這些添加的內容,都將是我們Runtime的操作對象。下方那幾個TestClass的類目稍後在使用Runtime時再進行介紹。

  

  

  

 

二、RuntimeKit的封裝

接下來我們就來看看RuntimeKit中的內容,其中對Runtime常用的方法進行了簡單的封裝。主要是動態擷取類的一些屬性和方法的,以及動態方法添加和方法交換的。本部分的乾貨還是不少的。

1、擷取類名

動態擷取類名是比較簡單的,使用class_getName(Class)就可以在運行時來擷取類的名稱。class_getName()函數返回的是一個char類型的指標,也就是C語言的字串類型,所以我們要將其轉換成NSString類型,然後再返回出去。下方的+fetchClassName:方法就是我們封裝的擷取類名的方法,如下所示:

  

 

2、擷取成員變數

下方這個+fetchIvarList:這個方法就是我們封裝的擷取類的成員變數的方法。當然我們在擷取成員變數時,可以用ivar_getTypeEncoding()來擷取相應成員變數的類型。使用ivar_getName()來擷取相應成員變數的名稱。下方就是對擷取成員變數的功能的封裝。返回的是一個數組,數組的元素是一個字典,而字典中儲存的就是相應成員變數的名稱和類型。

  

下方就是調用上述方法擷取的TestClass類的成員變數。當然在運行時就沒有什麼私人和公有之分了,只要是成員變數就可以擷取到。在OC中的給類新增成員屬性其實就是添加了一個成員變數和getter以及setter方法。所以擷取的成員列表中肯定帶有成員屬性,不過成員屬性的名稱前方添加了底線來與成員屬性進行區分。我們也可以擷取成員變數的類型,下方的_var1是NSInteger類型,動態擷取到的是q字母,其實是NSInteger的符號。而i就表示int類型,c表示Bool類型,d表示double類型,f則就表示float類型。當然這些基本類型都是由一個字母代替的,如果是參考型別的話,則直接就是一個字串了,比如NSArray類型就是"@NSArray"。

  

 

3.擷取成員屬性

上面擷取的是類的成員變數,那麼下方這個+fetchPropertyList:擷取的就是成員屬性。當然此刻擷取的只包括成員屬性,也就是那些有setter或者getter方法的成員變數。下方主要是使用了class_copyPropertyList(Class,&count)來擷取的屬性列表,然後通過for迴圈通過property_getName()來擷取每個屬性的名字。當然使用property_getName()擷取到的名字依然是C語言的char類型的指標,所以我們還需要將其轉換成NSString類型,然後放到數組中一併返回。如下所示:

  

下方這個就是調用上述方法擷取的TestClass的所有的屬性,當然dynamicAddProperty是我們使用Runtime動態給TestClass添加的,所以也是可以擷取到的。當然我們擷取到的屬性的名稱為了與其對應的成員變數進行區分,成員屬性的名字前邊是沒有底線的。

  

 

4、擷取類的執行個體方法

接下來我們就來封裝一下擷取類的執行個體方法列表的功能,下方這個+fetchMethodList:就是我們封裝的擷取類的執行個體方法列表的函數。在下方函數中,通過class_copyMethodList()方法擷取類的執行個體方法列表,然後通過for迴圈使用method_getName()來擷取每個方法的名稱,然後將方法的名稱轉換成NSString類型,儲存到數組中一併返回。具體代碼如下所示:

  

下方這個就是上述方法在TestClass上啟動並執行結果,其中列印了TestClass類的所有執行個體方法,當然其中也必須得包含成員屬性的getter和setter方法。當然TestClass類目中的方法也是必須能擷取到的。結果如下所示:

  

 

5、擷取協議列表

下方是擷取我們類所遵循協議列表的方法,主要使用了class_copyProtocolList()來擷取列表,然後通過for循序使用protocol_getName()來擷取協議的名稱,最後將其轉換成NSString類型放入數組中返回即可。

  

下方就是我們擷取到的TestClass類所遵循的協議列表:

  

 

6、動態添加方法實現

下方就是動態往相應類上添加方法以及實現。下方的+addMethod方法有三個參數,第一個參數是要添加方法的類,第二個參數是方法的SEL,第三個參數則是提供方法實現的SEL。稍後在訊息發送和訊息轉寄時會用到下方的方法。下方主要是使用class_getInstanceMethod()和method_getImplementation()這兩個方法相結合擷取相應SEL的方法實現。下方的IMP其實就是Implementation的方法縮寫,擷取到相應的方法實現後,然後再調用class_addMethod()方法將IMP與SEL進行綁定即可。具體做法如下所示。

  

 

7、方法實現交換

下方就是講類的兩個方法的實現進行交換。如果將MethodA與MethodB的方法實現進行交換的話,調用MethodA時就會執行MethodB的內容,反之亦然。

  

下方這段代碼就是對上述方法的測試。下方是TestClass的一個類目,在該類目中將類目中的方法與TestClass中的方法進行了替換。也就是將method1與method2進行了替換,替換後在method2中調用的method2其實就是調用的method1。在第三方庫中,經常會使用該特性,已達到AOP編程的目的。

  

 

三、屬性關聯

屬性關聯說白了就是在類目中動態為我們的類添加相應的屬性,如果看過之前發布的對Masonry架構源碼解析的部落格的話,對下方的屬性關聯並不陌生。在Masonry架構中就利用Runtime的屬性關聯在UIView的類目中給UIView添加了一個約束數組,用來記錄添加在當前View上的所有約束。下方就是在TestClass的類目中通過objc_getAssociatedObject()和objc_setAssociatedObject()兩個方法為TestClass類添加了一個dynamicAddProperty屬性。上面我們擷取到的屬性列表中就含有該動態添加的成員屬性。

下方就是屬性關聯的具體代碼,如下所示。

  

 

四、訊息處理與訊息轉寄

在Runtime中不得不提的就是OC的訊息處理和訊息轉寄機制。當然網上也有不少相關資料,本篇部落格為了完整性,還是要聊一下訊息處理與訊息轉寄的。當你調用一個類的方法時,先在本類中的方法緩衝列表中進行查詢,如果在緩衝列表中找到了該方法的實現,就執行,如果找不到就在本類中的方列表中進行尋找。在本類方列表中尋找到相應的方法實現後就進行調用,如果沒找到,就去父類中進行尋找。如果在父類中的方法列表中找到了相應方法的實現,那麼就執行,否則就執行下方的幾步

當調用一個方法在緩衝列表,本類中的方法列表以及父類的方法列表找不到相應的實現時,到程式崩潰階段中間還會有幾步讓你來挽救。接下來就來看看這幾步該怎麼走。

1.訊息處理(Resolve Method)

當在相應的類以及父類中找不到類方法實現時會執行+resolveInstanceMethod:這個類方法。該方法如果在類中不被重寫的話,預設返回NO。如果返回NO就表明不做任何處理,走下一步。如果返回YES的話,就說明在該方法中對這個找不到實現的方法進行了處理。在該方法中,我們可以為找不到實現的SEL動態添加一個方法實現,添加完畢後,就會執行我們添加的方法實現。這樣,當一個類調用不存在的方法時,就不會崩潰了。具體做法如下所示:

  

 

2、訊息快速轉寄

如果不對上述訊息進行處理的話,也就是+resolveInstanceMethod:返回NO時,會走下一步訊息轉寄,即-forwardingTargetForSelector:。該方法會返回一個類的對象,這個類的對象有SEL對應的實現,當調用這個找不到的方法時,就會被轉寄到SecondClass中去進行處理。這也就是所謂的訊息轉寄。當該方法返回self或者nil, 說明不對相應的方法進行轉寄,那麼就該走下一步了。

  

 

3.訊息常規轉寄

如果不將訊息轉寄給其他類的對象,那麼就只能自己進行處理了。如果上述方法返回self的話,會執行-methodSignatureForSelector:方法來擷取方法的參數以及返回資料類型,也就是說該方法擷取的是方法的簽名並返回。如果上述方法返回nil的話,那麼訊息轉寄就結束,程式崩潰,報出找不到相應的方法實現的崩潰資訊。

在+resolveInstanceMethod:返回NO時就會執行下方的方法,下方也是講該方法轉寄給SecondClass,如下所示:

  

 

 今天的部落格就先到這兒吧,當然還有其他一些Runtime的東西本篇部落格並未涉及,如果以後解析那個開源庫的源碼時遇到了,我們在單獨聊。依照慣例,本篇部落格依附的Demo, 仍然會在Github上進行分享,下方是分享連結。

github源碼分享連結:https://github.com/lizelu/ObjCRuntimeDemo

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.