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方法的,不知道为何能够生成- -。

相关文章
相关标签/搜索