#ifndef NULLSAFE_ENABLED
前端
#define NULLSAFE_ENABLED 1
數組
#endif
框架
功能開關,條件編譯判斷是否認義了宏NULLSAFE_ENABLED
google
若未定義則NULLSAFE_ENABLED
定義爲 1 spa
若是須要關閉這個類的功能,設計
將NULLSAFE_ENABLED
置爲 0 code
或者在其餘能夠預編譯的地方將 NULLSAFE_ENABLED
置爲 0對象
#pragma clang diagnostic ignored "-Wgnu-conditional-omitted-operand"
字符串
這個是clang編譯器前端警告忽略get
可是忽略的內容很奇怪
看起來像是GCC編譯器時代的東西
後面跟的內容conditional-omitted-operand
條件語句忽略操做數,google之
相似 x ? x : y
等價於 x ? : y
http://docs.w3cub.com/gcc~7/conditionals/
沒想明白哪裏用獲得。
而後進行開關判斷 NULLSAFE_ENABLED
預編譯/條件編譯結束
經過Runtime
轉發機制實現將[NSNull null]
對象轉爲nil
通常狀況下咱們經過對空對象[NSNull null]
發送任何消息時,會致使崩潰
example://模擬原理
NSString * strValue ;
strValue = (id)[NSNull null];
NSLog(@"%ld",strValue.lenght);
例子是向字符串類型的strValue實例發送一個獲取字符串長度的消息
實質上是向[NSNull null]發送了字符串類型纔會響應的消息(方法)
而後,崩了。
咱們明白這個黑科技依然使用的是OC的Runtime優點,
原理和關鍵點在消息轉發機制上。
當咱們對Null對象發送消息,NullSafe做爲Null的Category,無侵入的對Null對象進行擴展
當向一個對象發送消息後,咱們有大概三次機會對發送消息這個動做進行修改。
前兩次就不說了,跟NullSafe的原理目的不太相關,NullSafe使用的是動態轉發的部分。
首先獲取方法簽名,也是爲了進行到下一步動態轉發作準備。
methodSignatureForSelector:
調用父類查看可否獲取方法簽名,這個狀況多是對Null發送null消息會成功獲取。
其餘狀況,獲取不到,那麼對經常使用的Foundation框架類遍歷驗證,
生成新的方法簽名,若是被遍歷的類響應消息(select),則從新生成方法簽名並進行下一步轉發,
若是獲取不到就不轉發了,直接報錯。
NSMethodSignature *signature = [super methodSignatureForSelector:selector];
if (!signature)
{
for (Class someClass in @[
[NSMutableArray class],
[NSMutableDictionary class],
[NSMutableString class],
[NSNumber class],
[NSDate class],
[NSData class]
])
{
@try
{
if ([someClass instancesRespondToSelector:selector])
{
signature = [someClass instanceMethodSignatureForSelector:selector];
break;
}
}
@catch (__unused NSException *unused) {}
}
}
return signature;
最後一步對方法進行轉發:
執行forwardInvocation:
方法實現動態轉發。
invocation.target = nil;
[invocation invoke];
target爲接收消息的對象,也就是[Null null],置爲nil。
調用invocation的invoke方法,就表明須要執行NSInvocation對象中指定對象的指定方法,而且傳遞指定的參數。
至此,向[NSNULL null]發送消息的行爲已經變成了向nil發送消息,解除了崩潰的隱患。
1.這個解決方案在我剛剛開始接觸的時候感受是很神奇的,由於直接拖進去就能夠用了,其中涉及的思路和想法很值得借鑑。
2.條件編譯的開關部分設計,Runtime的轉發思想,都或多或少的對Runtime的應用有必定啓發。