一般來說,系統提供的方法已經足夠開發了,但是有的時候有些需求用普通方法不好做。
如:在所有的viewcontroll 的viewwillappear:方法之前打個log
你可能會這么做:
1. 建一個uiviewcontroll 父類,重寫viewwillappear方法,調用super viewwillappear 方法之前加上log
2. 所有新建的uiviewcontroller 繼承第一步生成的
確實你是完成這樣的功能,可是你做了那么多的修改,基本每個uiviewcontroller都去修改了父類,這種方法太過于笨重了
?
本文提供了簡單地方法即可實現
我的理解中,object-c 的類調用方法是根據三個元素來定義的。?
1. 方法,代表類定義中一個方法類型(typedef struct objc_method *Method)
2. SEL 選擇器(typedef struct objc_selector *SEL),一個方法在運行時的名字,常見的有 [self performSelector:@selector(somemethod:) withObject:nil afterDelay:0.5]; @selector(somemethod:)作為方法的入口
3. 方法的實現入口(typedef id (*IMP)(id, SEL, …))
這三個元素確定了具體調用哪一個函數
?
直接看代碼
?
- #import?"UIViewController+Tracking.h"??
- #import?<objc/runtime.h>??
- ??
- @implementation?UIViewController?(Tracking)??
- ??
- +?(void)load?{??
- ????NSString?*className?=?NSStringFromClass(self.class);??
- ????NSLog(@"classname?%@",?className);??
- ????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(xxx_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);??
- ????????}??
- ????});??
- }??
?
我們category重寫了NSObject的 load 方法oc提供了objc/runtime.h類讓我們獲取這些東西,同時還提供了對類方法操作的函數
我們想的是,直接用一個方法替換掉系統的方法,然后把一些自定義的動作加到方法中
我們只想運行一次就夠了,所以使用了 dispatch_once(&onceToken, ^{ …… }
接下來給類添加了新方法
把新方法和系統方法替換
?
?
- #pragma?mark?-?Method?Swizzling??
- ??
- -?(void)xxx_viewWillAppear:(BOOL)animated?{??
- ????NSLog(@"viewWillAppear:?%@",?self);??
- ????[self?xxx_viewWillAppear:animated];??
- }??
但是新方法實現的時候,調用的是 [self xxx_viewwillAppear:animated]; 可能你會疑惑
?
這是因為我們在上面已經用xxx_viewwillAppear 和 viewwillAppear 互換了。所以實際上執行的是系統的viewwillAppear
這個時候可能你又有疑問了,為什么實現是- (void)xxx_viewWillAppear:(BOOL)animated{} 這樣的
這是因為 SEL swizzledSelector = @selector(xxx_viewWillAppear:); 拿的就是我們新寫的方法。
可以結合這篇博客看,配圖很容易懂
http://blog.csdn.net/yiyaaixuexi/article/details/9374411
?
以及這篇對SEL講的比較清楚
http://blog.csdn.net/fengsh998/article/details/8612969
代碼下載地址
https://github.com/holysin/Method_swizzle
1 ?Method?nameMethod =?class_getInstanceMethod(Class class,SEL name);//得到實例方法
參數:class類名 ?name 方法名
理解:通過類名和方法名來得到方法(c函數)
?
2 method_getImplementation(Method method)
參數:method 方法
理解:通過方法來得到它的實現
?
?
3 BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
理解:將所給的name(方法名)的方法實現,被imp(方法的實現)代替
cls:被添加方法的類
name:可以理解為方法名,這個貌似隨便起名,
imp:實現這個方法的函數?
types:一個定義該函數返回值類型和參數類型的字符串,這個具體會在后面講
?
4 class_replaceMethod(Class class,SEL ?B,?method_getImplementation(Method AMethod),?method_getTypeEncoding(Method AMethod));
參數:class :方法所屬的類 B:將被替換其實現的方法名 ? ?AMethod:由A生成的方法(c函數)
理解:用A的方法實現來代替B的方法實現
5 method_exchangeImplementations(SEL A, SEL B);
理解:交換兩個方法的實現
void swizzle_method(Class class,SEL originalSelector,SEL swizzledSelector) {
? ? Method originalMethod = class_getInstanceMethod(class, originalSelector);//得到實例方法
? ? Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
? ? BOOL didSwizzleMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
? ? if (didSwizzleMethod) {
? ? ? ? class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
? ? } else {
? ? ? ? method_exchangeImplementations(originalMethod, swizzledMethod);
? ? }
}
?
?