再nullsafe的github界面里,它的介绍是:
NullSafe is a simple category on NSNull that returns nil for
unrecognised messages instead of throwing an exception.
看了源码始终不得要领。有哪位能帮忙说说其中原理?
#import <objc/runtime.h>
#import <Foundation/Foundation.h>
#ifndef NULLSAFE_ENABLED
#define NULLSAFE_ENABLED 1
#endif
#pragma GCC diagnostic ignored "-Wgnu-conditional-omitted-operand"
@implementation NSNull (NullSafe)
#if NULLSAFE_ENABLED
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
@synchronized([self class])
{
//look up method signature
NSMethodSignature *signature = [super methodSignatureForSelector:selector];
if (!signature)
{
//not supported by NSNull, search other classes
static NSMutableSet *classList = nil;
static NSMutableDictionary *signatureCache = nil;
if (signatureCache == nil)
{
classList = [[NSMutableSet alloc] init];
signatureCache = [[NSMutableDictionary alloc] init];
//get class list
int numClasses = objc_getClassList(NULL, 0);
Class *classes = (Class *)malloc(sizeof(Class) * (unsigned long)numClasses);
numClasses = objc_getClassList(classes, numClasses);
//add to list for checking
NSMutableSet *excluded = [NSMutableSet set];
for (int i = 0; i < numClasses; i++)
{
//determine if class has a superclass
Class someClass = classes[i];
Class superclass = class_getSuperclass(someClass);
while (superclass)
{
if (superclass == [NSObject class])
{
[classList addObject:someClass];
break;
}
[excluded addObject:NSStringFromClass(superclass)];
superclass = class_getSuperclass(superclass);
}
}
//remove all classes that have subclasses
for (Class someClass in excluded)
{
[classList removeObject:someClass];
}
//free class list
free(classes);
}
//check implementation cache first
NSString *selectorString = NSStringFromSelector(selector);
signature = signatureCache[selectorString];
if (!signature)
{
//find implementation
for (Class someClass in classList)
{
if ([someClass instancesRespondToSelector:selector])
{
signature = [someClass instanceMethodSignatureForSelector:selector];
break;
}
}
//cache for next time
signatureCache[selectorString] = signature ?: [NSNull null];
}
else if ([signature isKindOfClass:[NSNull class]])
{
signature = nil;
}
}
return signature;
}
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
[invocation invokeWithTarget:nil];
}
#endif
@end
我不知道這個庫,我稍微看了一下。
簡單點說,當我們給一個NSNull物件發送訊息的話,可能會崩潰(null是有記憶體的),而發送給nil的話,是不會崩潰的。
作者就是使用了這麼一個原理,把發送給NSNull的而NSNull又無法處理的訊息經過如下幾步處理:
建立一個方法緩存,這個緩存會緩存項目中類別的所有類別名稱。
遍歷緩存,尋找是否已經有可以執行此方法的類別。
如果有的話,回傳這個
NSMethodSignature
。如果沒有的話,返回nil,接下來會走
forwardInvocation:
方法。[invocation invokeWithTarget:nil];
將訊息轉發給nil。那麼,如何判斷NSNull無法處理這個訊息呢,在OC中,系統如果對某個實例發送訊息之後,它(及其父類)無法處理(比如,沒有這個方法等),系統就會發送
methodSignatureForSelector
消息,如果这个方法返回非空,那么就去执行返回的方法,如果为nil,则发送forwardInvocation
訊息。這樣就完成整個轉發鏈了。
題外話:
一般來說,我們不應該在我們的專案中使用NSNull類別(大部分NSNull類別的來源來自於介面的回傳),而使用nil,在來源上,就應該堵住(要嘛你解析到null進行處理,要么和你的服務端說,不要給我返回null)。
講解很清楚 66666