大多數(shù)情況下,系統(tǒng)類為我們提供的方法已經(jīng)能夠滿足開發(fā)的需求,但有時(shí)候需求中的功能使用原生方法很難實(shí)現(xiàn),或者系統(tǒng)類提供的原生方法存在bug.
比如:我們要在所有的viewcontroll 的viewwillappear:方法之前通過NSLog輸出方法的信息。
實(shí)現(xiàn)的方法有多種,你可以這么做:
- 建一個(gè)uiviewcontroll 父類,重寫viewwillappear方法,調(diào)用super viewwillappear 方法之前加上NSlog
- 所有新建的UIViewcontroller 繼承第一步生成的
上面方式可以完成該功能,但你做了那么多的修改,基本每個(gè)uiviewcontroller都去修改了父類,這種方法太過于消耗性能。
但其實(shí)有更簡單和高效的方法。
OC中的類調(diào)用方法是通過三種方式來進(jìn)行調(diào)用。
- 方法,代表類定義中一個(gè)方法類型(typedef struct objc_method *Method)
- SEL 選擇器(typedef struct objc_selector *SEL),一個(gè)方法在運(yùn)行時(shí)的名字,常見的有 [self performSelector:@selector(somemethod:) withObject:nil afterDelay:0.5]; @selector(somemethod:)作為方法的入口
- 方法的實(shí)現(xiàn)入口IMP及SEL
這三種方法確定了具體調(diào)用哪一個(gè)函數(shù)
下面是實(shí)現(xiàn)代碼
#import"UIViewController+Tracking.h"
#import<objc/runtime.h>
@implementationUIViewController(Tracking)
+(void)load{
NSString*className=NSStringFromClass(self.class);
NSLog(@"classname%@",className);
staticdispatch_once_tonceToken;
dispatch_once(&onceToken,^{
Classclass=[selfclass];
//Whenswizzlingaclassmethod,usethefollowing:
//Classclass=object_getClass((id)self);
SELoriginalSelector=@selector(viewWillAppear:);
SELswizzledSelector=@selector(xxx_viewWillAppear:);
MethodoriginalMethod=class_getInstanceMethod(class,originalSelector);
MethodswizzledMethod=class_getInstanceMethod(class,swizzledSelector);
BOOLdidAddMethod=
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);
}
});
}
我們想要重寫NSObject的 load 方法,oc給我們提供了objc/runtime.h類讓我們獲取這些東西,同時(shí)還提供了對(duì)類方法操作的函數(shù)
我們想要實(shí)現(xiàn)的是,直接用一個(gè)方法替換掉系統(tǒng)的方法,然后把一些自定義的動(dòng)作加到方法中。我們只想運(yùn)行一次就夠了,所以使用了 dispatch_once(&onceToken, ^{ …… }
接下來給類添加了新方法
把新方法和系統(tǒng)方法替換
#pragmamark-Runtime
-(void)bb_viewWillAppear:(BOOL)animated{
NSLog(@"viewWillAppear:%@",self);
[self bb_viewWillAppear:animated];
}
這樣,新方法在實(shí)現(xiàn)的時(shí)候,調(diào)用的是 [self bb_viewwillAppear:animated]; 因?yàn)樵谏厦嬉呀?jīng)用bb_viewwillAppear 和 viewwillAppear 互換了。所以實(shí)際上執(zhí)行的是系統(tǒng)的viewwillAppear
這個(gè)時(shí)候可能你又有疑問了,為什么實(shí)現(xiàn)是- (void)bb_viewWillAppear:(BOOL)animated{} 這樣的
這是因?yàn)?SEL swizzledSelector = @selector(bb_viewWillAppear:); 拿的就是我們新寫的方法。