大家都知道oc是動態語言,是runtime的,怎么體現他的動態性呢,今天用SEL來分析下,之前分享過鉤子其實就可以看出來了,現在具體看看OC是怎么通過SEL找到函數的。
[self performSelector:@selector(abcd)];
[self abcd];? //這種方法可能都會編譯不過去
假如類方法里面我們便沒有寫adcd方法,這樣就會奔潰了,oc是怎么做的呢
該方法調用后,OC會發出objc_msgSend,將消息傳給本來查找本類中的SEL是否存在Method
假設本類不存在會查找工程里是否有分類提供了該方法
假設分類中也沒有該方法,系統會將消息轉發給其父類,父類收到消息后也會執行上面的操作,找方法,沒找到再往上轉發消息
假設最終都沒有找到,就會執行最后的消息轉發(message forwarding)操作
如果轉發出去都沒人接收的話,NSObject中的doesNotRecognizeSelector就選擇拋出異常了,也就是我們看到的crash
上面的過程有點復雜,大家會覺得這樣很慢,第一次的確很慢,所性objc_msgSend會將匹配結果寫到映射表中緩存起來,每個類都有這樣的一塊緩存
整個過程就是上面將的那樣,我們講一下第4項消息轉發怎么處理吧:
消息轉發還分兩個階段:
1.動態方法解析:意思就是說詢問接受者要不要增加個方法來實現該函數
+ (BOOL)resolveInstanceMethod:(SEL)selecotor? //對象方法
+ (BOOL)resolveClassMethod:(SEL)sel? ? ? ? ? ? //類方法
在方法里可以動態給類增加一個方法Method
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self performSelector:@selector(abcd)];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSString *selname = NSStringFromSelector(sel);
if ([selname isEqualToString:@"abcd"]) {
class_addMethod(self, sel, class_getMethodImplementation([self class], @selector(oklala)), method_getTypeEncoding(class_getInstanceMethod([self class], @selector(oklala))));
return YES;
}
return [super resolveInstanceMethod:sel];
}
- (void)oklala {
NSLog(@"oklala");
}
2.完整的消息轉發:看看還有沒有別的對象要處理,有轉出,沒有的話封裝成NSInvocation對象處理
1)假如可以轉給別的對象處理:
- (id)forwardingTargetForSelector:(SEL)aSelector
可以將要轉發的對象返回。
@interface abcdCaller : NSObject
@end
@implementation abcdCaller
- (void)abcd {
NSLog(@"~~~~~~");
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self performSelector:@selector(abcd)];
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(@"!!!!!!!!!!!!!");
return [[abcdCaller alloc] init];
}
@end
2)假如沒有對象可以轉發(完整消息轉發)
- (void)forwardInvocation:(NSInvocation *)anInvocation
最后可以處理消息的機會
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self performSelector:@selector(abcd)];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *sig = [NSMethodSignature signatureWithObjCTypes:"@@:"];
return sig;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"~~~~%@", [anInvocation description]);
}
---------------------------------------------------凌亂的分割線-------------------------------------------------
假設你的消息轉發不處理,那么在第5步捕獲異常,也是不會奔潰的
@try {
[self performSelector:@selector(abcd)];
}
@catch (NSException *exception) {
NSLog(@"~~~~~~~~");
}
@finally {
}