iOS Runtime緩存
Objective-C 是一個動態語言,這意味着它不只須要一個編譯器,也須要一個運行時系統來動態得建立類和對象、進行消息傳遞和轉發。理解 Objective-C 的 Runtime 機制能夠幫咱們更好的瞭解這個語言,適當的時候還能對語言進行擴展,從系統層面解決項目中的一些設計或技術問題。瞭解 Runtime ,要先了解它的核心 - 消息傳遞 (Messaging)。markdown
Runtime objc_msgSend執行流程 在OC中的方法調用,其實都是轉換爲objc_msgSend函數的調用,給receiver(方法調用者)發送一條消息(selector 方法名) objc_msgSend的執行流程能夠分爲3個階段 消息發送 動態方法解析 消息轉發 ps:若是這三個階段都找不到方法的話就會報經典錯誤 unrecognized selector sent to instance函數
經過receiver(消息接收者)的isa 指針 找到receiver的Class 在Class的cache(方法緩存)的散列表中尋找對應的IMP(方法實現) 若是在cache(方法緩存)中沒有找到對應的IMP,就繼續在Class的methodlist(方法列表)中找對應的selector,若是找到,填充到cache(方法緩存)中,並返回selector; 若是在class中,沒有找到這個selector,就繼續在它的superClass中的cache(方法緩存)中查找,若是查到緩存到Class中的cache(方法緩存)中 若是沒有查到,就在superClass中的methodlist中查找,而且緩存到Class中的cache(方法緩存)中 若是仍是沒有找到IMP(方法實現),判斷是否仍是有superClass,若是有重複4,5步驟,若是沒有則開始進入到動態方法解析階段 動態方法解析 源碼是否曾經有過動態解析 若是沒有 則看開發者有沒有實現+resolveInstanceMethod:,resolveClassMethod:方法來動態解析方法 若是實現了該方法,標記爲已經動態解析。會從新回到消息發送流程中(在Class的cache中查找方法)這一步開始執行。 若是已經動態解析過 則會進入下一步 消息轉發階段。spa
例子:設計
ZQPerson * p = [[ZQPerson alloc]init];
[p test];
打印結果:-[ZQPerson other]
複製代碼
person類中沒有實現test方法 而是在resolveInstanceMethod中動態指向了other方法的實現 person.h指針
@interface ZQPerson : NSObject
- (void)test;
@end
person.m
- (void)other{
NSLog(@"%s",__func__);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel{
Method otherMethod = class_getInstanceMethod(self, @selector(other));
if (sel == @selector(test)) {
class_addMethod(self, sel, method_getImplementation(otherMethod), method_getTypeEncoding(otherMethod));
return YES;
}
return [super resolveInstanceMethod:sel];
}
複製代碼
消息轉發 在消息發送過程當中和動態方法解析過程當中都沒有找到具體的實現,這樣就會進入消息轉發。 消息轉發:將消息轉發給別人。 將嘗試調用(id)forwardingTargetForSelector:(SEL)aSelector方法 返回值部位nil,就會調用obj_msgSend(返回值,SEL), 若是forwardingTargetForSelector返回爲nil,則要實現methodSignatureForSelector,forwardInvocation,這兩個方法。code
方法簽名:返回值類型,參數類型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if (aSelector == @selector(test)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
//NSInvocation封裝了一個方法調用,包括:方法調用者、方法名、方法參數
// anInvocation.target // 方法調用者
// anInvocation.selector 方法名
// [anInvocation getArgument:NULL atIndex:0]; 參數
- (void)forwardInvocation:(NSInvocation *)anInvocation{
anInvocation.target = [[ZQCat alloc]init];//指定消息轉發接收的對象
[anInvocation invoke];//執行方法
//或者這樣寫
[anInvocation invokeWithTarget:[[ZQCat alloc]init]];
}
複製代碼
關注下面的標籤,發現更多類似文章orm