Objective-C是一門動態語言,一個函數是由一個selector(SEL),和一個implement(IML)組成的。
執行一個方法時若是系統找不到方法會給幾回機會尋找方法,實在沒有此方法就會拋出異常。數組
由圖可見緩存
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector - (void)forwardInvocation:(NSInvocation *)anInvocation 這兩個函數是最後一個尋找IML的機會。這個函數讓重載方有機會拋出一個函數的簽名,再由後面的forwardInvocation:去執行。
源碼解讀函數
#ifndef NULLSAFE_ENABLED #define NULLSAFE_ENABLED 1 #endif // 忽略warning // 三木運算符忽略中間一木致使的警告 #pragma GCC diagnostic ignored "-Wgnu-conditional-omitted-operand"
關閉警告spa
// 調用methodSignatureForSelector 方法 - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { // 保持原子性,添加同步鎖,防止被修改 @synchronized([self class]) { } }
// 本類父類種尋找是否擁有此方法,擁有方法則直接返回 signature NSMethodSignature *signature = [super methodSignatureForSelector:selector]; if (!signature) { } return signature;
// 本類父類種尋找是否擁有此方法 NSMethodSignature *signature = [super methodSignatureForSelector:selector]; if (!signature) { // 方法列表 static NSMutableSet *classList = nil; // 緩存方法字典 static NSMutableDictionary *signatureCache = nil; if (signatureCache == nil) { classList = [[NSMutableSet alloc] init]; signatureCache = [[NSMutableDictionary alloc] init]; //get class list /* 分析:該函數的做用是獲取已經註冊的類,它須要傳入兩個參數,第一個參數 buffer :已分配好內存空間的數組,第二個參數 bufferCount :數組中可存放元素的個數,返回值是註冊的類的總數。 當參數 bufferCount 值小於註冊的類的總數時,獲取到的是註冊類的集合的任意子集 第一個參數傳 NULL 時將會獲取到當前註冊的全部的類,此時可存放元素的個數爲0,所以第二個參數可傳0,返回值爲當前註冊的全部類的總數。 */ // 獲取項目中全部類的個數 int numClasses = objc_getClassList(NULL, 0); // 調整一個classes的大小 = 獲取一個class的 size * 全部的class Class *classes = (Class *)malloc(sizeof(Class) * (unsigned long)numClasses); // 獲取項目中class的個數 numClasses = objc_getClassList(classes, numClasses); // 初始化被排除的 NSMutableSet NSMutableSet *excluded = [NSMutableSet set]; for (int i = 0; i < numClasses; i++) { // 判斷classes【i】 是否有superclass Class someClass = classes[i]; Class superclass = class_getSuperclass(someClass); // 循環找出 someClass 的全部的superclass while (superclass) { // 當superclass存在 判斷是否等於 NSObject if (superclass == [NSObject class]) { // 等於 NSObject 加入 classList [classList addObject:someClass]; break; } [excluded addObject:NSStringFromClass(superclass)]; superclass = class_getSuperclass(superclass); } } // 上面循環走完以後 查找到全部繼承自NSObject的類 // 基於 NSObject 的類 中 刪除 不基於 NSObject 類 for (Class someClass in excluded) { [classList removeObject:someClass]; } // 釋放上面建立的 classes free(classes); }
通過上面代碼獲取項目中的類的列表和緩存
// check implementation cache first NSString *selectorString = NSStringFromSelector(selector); signature = signatureCache[selectorString]; if (!signature) { for (Class someClass in classList) { // 判斷 這個基於NSObject類的子類是否可以響應傳入的方法 if ([someClass instancesRespondToSelector:selector]) { // someClass類可以響應selector方法 // 返回NSMethodSignature對象,這個對象包含被標示的實例方法的描述。 signature = [someClass instanceMethodSignatureForSelector:selector]; break; } } // cache for next time signatureCache[selectorString] = signature ?: [NSNull null]; } else if ([signature isKindOfClass:[NSNull class]]) { // 緩存是NSNull類型的話 將須要執行的方法置爲nil signature = nil; }
// forwardInvocation:將選擇器轉發給一個真正實現了該消息的對象。 - (void)forwardInvocation:(NSInvocation *)invocation { // 將target = nil ,不發送 invocation.target = nil; [invocation invoke]; }
總結:
當咱們給一個NSNull對象發送消息的話,可能會崩潰(null是有內存的),而發送給nil的話,是不會崩潰的。code
做者就是使用了這麼一個原理,把發送給NSNull的而NSNull又沒法處理的消息通過以下幾步處理:對象
那麼,如何判斷NSNull沒法處理這個消息呢,在OC中,系統若是對某個實例發送消息以後,它(及其父類)沒法處理(好比,沒有這個方法等),系統就會發送methodSignatureForSelector消息,若是這個方法返回非空,那麼就去執行返回的方法,若是爲nil,則發送forwardInvocation消息。blog
這樣就完成整個轉發鏈了。繼承