iOS開發之 Method Swizzling 深入淺出

來源:互聯網
上載者:User

標籤:iOS   Swift   Objective-C   GitHub   Apple   

<p align="center">
<img src ="https://raw.githubusercontent.com/DotzuX/Notes/master/logo.jpeg"/>
</p>

iOS開發之 Method Swizzling 深入淺出

只要善用Google,網上有很多關於Method Swizzling的Demo,在這裡我就不打算貼代碼了,主要介紹下概念,原理,注意事項等等。

開發需求

如果產品經理突然說:"在所有頁面添加統計功能,也就是使用者進入這個頁面就統計一次"。我們會想到下面的一些方法:

  • 手動添加

直接簡單粗暴的在每個控制器中加入統計,複製、粘貼、複製、粘貼...
上面這種方法太Low了,消耗時間而且以後非常難以維護,會讓後面的開發人員罵死的。

  • 繼承

我們可以使用繼承的方式來解決這個問題。建立一個基類,在這個基類中添加統計方法,其他類都繼承自這個基類。

然而,這種方式修改還是很大,而且定製性很差。以後有新人加入之後,都要囑咐其繼承自這個基類,所以這種方式並不可取。

  • Category

我們可以為UIViewController建一個Category,然後在所有控制器中引入這個Category。當然我們也可以添加一個PCH檔案,然後將這個Category添加到PCH檔案中。

  • Method Swizzling

我們可以使用蘋果的“黑魔法”Method SwizzlingMethod Swizzling本質上就是對IMPSEL進行交換。

先瞭解幾個概念Selectors, Methods, & Implementations

Objective-C的運行時中,selectors, methods, implementations 指代了不同概念,然而我們通常會說在訊息發送過程中,這三個概念是可以相互轉換的。 下面是蘋果 Objective-C Runtime Reference中的描述:

  • Selector(typedef struct objc_selector *SEL):在運行時 Selectors 用來代表一個方法的名字。Selector是一個在運行時被註冊(或映射)的C類型字串。Selector由編譯器產生並且在當類被載入進記憶體時由運行時自動進行名字和實現的映射。

  • Method(typedef struct objc_method *Method):方法是一個不透明的用來代表一個方法的定義的類型。

  • Implementation(typedef id (*IMP)(id, SEL,...)):這個資料類型指向一個方法的實現的最開始的地方。該方法為當前CPU架構使用標準的C方法調用來實現。該方法的第一個參數指向調用方法的自身(即記憶體中類的執行個體對象,若是調用類方法,該指標則是指向元類對象(metaclass)。第二個參數是這個方法的名字selector,該方法的真正參數緊隨其後。

理解 selector, method, implementation 這三個概念之間關係的最好方式是:在運行時,類(Class)維護了一個訊息分發列表來解決訊息的正確發送。每一個訊息列表的入口是一個方法(Method),這個方法映射了一對索引值對,其中索引值是這個方法的名字 selector(SEL),值是指向這個方法實現的函數指標 implementation(IMP)Method swizzling 修改了類的訊息分發列表使得已經存在的 selector 映射了另一個實現 implementation,同時重新命名了原生方法的實現為一個新的 selector

Method Swizzling原理

Method Swizzing是發生在運行時的,主要用於在運行時將兩個Method進行交換,我們可以將Method Swizzling代碼寫到任何地方,但是只有在這段Method Swilzzling代碼執行完畢之後互換才起作用。

Method Swizzling 使用注意類簇設計模式

在iOS中NSNumber、NSArray、NSDictionary等這些類都是類簇(Class Clusters),一個NSArray的實現可能由多個類組成。
所以如果想對NSArray進行Swizzling,必須擷取到其“真身”進行Swizzling,直接對NSArray進行操作是無效的。

下面列舉了NSArray和NSDictionary本類的類名,可以通過Runtime函數取出本類。

類名 真身
NSArray __NSArrayI
NSMutableArray __NSArrayM
NSDictionary __NSDictionaryI
NSMutableDictionary __NSDictionaryM
注意要點
  • Swizzling應該總在+load中執行
  • Swizzling應該總是在dispatch_once中執行
  • Swizzling在+load中執行時,不要調用[super load]。如果多次調用了[super load],可能會出現“Swizzle無效”的假象,原理見:

Swift 自訂類中使用 Method Swizzling

要在 Swift 自訂類中使用 Method Swizzling 有兩個必要條件:

  • 包含 Swizzle 方法的類需要繼承自 NSObject
  • 需要 Swizzle 的方法必須有動態屬性(dynamic attribute)

註:對於 Swift 的自訂類,因為預設並沒有使用 Objective-C 運行時,因此也沒有動態派發的方法列表,所以如果要 Swizzle 的是 Swift 類型的方法的話,是需要將原方法和替換方法都加上 dynamic 標記,以指明它們需要使用動態派發機制。當然類也要繼承自 NSObject。

再註:下面這個例子使用了 Objective-C 的動態派發,對於 NSObject 的子類(UIViewController)是可以直接使用的,並不是 Swift 中自訂的類,因此沒有加 dynamic 標記也是可以的。

Method Swizzling 中 Objective-C 與 Swift 的異同
區別 Objective-C Swift
Runtime 標頭檔 #import &lt;objc/runtime.h&gt; 不需要
Swizzling 調用處 load 方法 initialize 方法

註:load 方法只在 Objective-C 裡有,而且不能在 Swift 裡重載,不管怎麼試都會報編譯錯誤。接下來執行 Swizzle 最好的地方就是 initialize了,這是調用第一個方法前的地方。

因為 Swizzling 會改變全域狀態,所以我們需要在運行時採取一些預防措施。GCD 的dispatch_once 可以保證操作的原子性,確保代碼只被執行一次,不管有多少個線程。

Method Swizzling 實際應用APM(應用效能管理)

網路監控的原理,應該就是hook NSURLConnection , NSURLSession。崩潰收集的原理,應該就是hook NSException

  • https://newrelic.com/ 國外行業老大
  • http://www.tingyun.com/ 國內聽雲
  • http://www.oneapm.com/ 國內OneAPM
  • http://apm.netease.com/ 國內網易
國外資料

iOS開發之 Method Swizzling 深入淺出

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.