iOS NullSafe解析

預編譯部分:

#ifndef NULLSAFE_ENABLED前端

#define NULLSAFE_ENABLED 1數組

#endif框架

功能開關,條件編譯判斷是否認義了宏NULLSAFE_ENABLEDgoogle

若未定義則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發送消息,解除了崩潰的隱患。

 

PS.

1.這個解決方案在我剛剛開始接觸的時候感受是很神奇的,由於直接拖進去就能夠用了,其中涉及的思路和想法很值得借鑑。

2.條件編譯的開關部分設計,Runtime的轉發思想,都或多或少的對Runtime的應用有必定啓發。

3.在獲取生成方法簽名時,遍歷了幾個Foundation框架經常使用的類型,基本上對應了JSON轉化當中的大部分經常使用類型,然鵝...我偷偷試了一下NSSet…set的allObjects方法,也就是NSSet類型置爲空類型而後執行allObjects方法,很神奇,斷點來看竟然遍歷到了可變數組的類型,生成了方法簽名,可是NSArray 和 NSMutableArray都應該是沒有allObjects方法的,不知道爲何能夠生成- -。

相關文章
相關標籤/搜索