日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Objective-C Runtime (三):Method Swizzling(方法替换)

發布時間:2025/3/17 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Objective-C Runtime (三):Method Swizzling(方法替换) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Objective-C Runtime (三):Method Swizzling(方法替換)

Method Swizzling是一種改變改變一個'selector'的實際實現的技術。通過這一技術,我們可以在運行時通過修改類的分發表中selector對應的函數,來修改方法的實現。 實現圖解如下圖:

從上圖中,我們可以看到,使用Method Swizzling本質上是將selectorC的方法實現IMPc與selectorN的方法實現IMPn交換了,當我們調用selectorC,也就是給對象發送selectorC消息時,所查找到的對應的方法實現就是IMPn而不是IMPc了。

那Method Swizzling在什么情況下可以用到了? 例如:我們接到一個需求:對 App 的用戶行為進行追蹤和分析。簡單來說,就是,就是當用戶進入某個界面或者點擊某個按鈕時,記錄這個事件。

最粗暴的方式,就是在每個 viewDidAppear 里添加記錄事件的代碼。這種方式缺點是很明顯的,它破壞了代碼的干凈整潔。因為記錄事件的代碼本身不屬于原有代碼的主要邏輯。隨著項目擴大、代碼增加,我們的原有代碼里會到處分布著記錄事件的代碼。這時,要找到一段事件記錄的代碼會變得困難,也很容易忘記添加事件記錄的代碼。

我們可能會想到使用繼承或類別,在重寫的方法里添加事件記錄的代碼。但這樣也會帶來新的問題:

  • 我們無法控制別人如何去實例化我們的子類;
  • 對于類別,我們沒辦法調用到原來的方法實現。大多時候,我們重寫一個方法只是為了添加一些代碼,而不是完全取代它;
  • 如果有兩個類別都實現了相同的方法,運行時沒法保證哪一個類別的方法會給調用;
  • 每個 ViewController 里的 ButtonClick 方法命名不可能都一樣。
  • 我了解決以上的問題,我們可以使用Method Swizzling,如以下代碼所示:

    #import "UIViewController+Tracking.h" #import <objc/runtime.h> @implementation UIViewController (Tracking)+ (void)load {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{Class class = [self class];// When swizzling a class method, use the following:// Class class = object_getClass((id)self);SEL originalSelector = @selector(viewWillAppear:);SEL swizzledSelector = @selector(track_viewWillAppear:);Method originalMethod = class_getInstanceMethod(class, originalSelector);Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);BOOL didAddMethod =class_addMethod(class,originalSelector,method_getImplementation(swizzledMethod),method_getTypeEncoding(swizzledMethod));if (didAddMethod) {class_replaceMethod(class,swizzledSelector,method_getImplementation(originalMethod),method_getTypeEncoding(originalMethod));} else {method_exchangeImplementations(originalMethod, swizzledMethod);}});}- (void)track_viewWillAppear:(BOOL)animated {[self track_viewWillAppear:animated];NSLog(@"viewWillAppear: %@", self); }@end 復制代碼

    從上面代碼可以看出,我們通過method swizzling修改了UIViewController的@selector(viewWillAppear:)對應的函數指針,使其實現指向了我們自定義的track_viewWillAppear:的實現。這樣,當UIViewController及其子類的對象調用viewWillAppear時,都會打印一條日志信息。

    上面代碼需要解釋的問題: class_addMethod:要先嘗試添加原 selector 是為了做一層保護,因為如果這個類沒有實現 originalSelector ,但其父類實現了,那 class_getInstanceMethod 會返回父類的方法。這樣 method_exchangeImplementations 替換的是父類的那個方法,這當然不是你想要的。所以我們先嘗試添加 orginalSelector ,如果已經存在,再用 method_exchangeImplementations 把原方法的實現跟新的方法實現給交換掉。

    注意事項 Swizzling通常被稱作是一種黑魔法,容易產生不可預知的行為和無法預見的后果。濫用可能會造成很多問題,如果遵從以下幾點預防措施的話,還是比較安全的:

  • Swizzling應該總是在+load中執行;
  • Swizzling應該總是在dispatch_once中執行;
  • 總是調用方法的原始實現(除非有更好的理由不這么做):API提供了一個輸入與輸出約定,但其內部實現是一個黑盒。Swizzle一個方法而不調用原始實現可能會打破私有狀態底層操作,從而影響到程序的其它部分;
  • 避免沖突:給自定義的分類方法加前綴,從而使其與所依賴的代碼庫不會存在命名沖突。
  • 參考:

  • nshipster.com/method-swiz…
  • 總結

    以上是生活随笔為你收集整理的Objective-C Runtime (三):Method Swizzling(方法替换)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。