iOS NullSafe解析

预编译部分:

#ifndef NULLSAFE_ENABLED

#define NULLSAFE_ENABLED 1

#endif

功能开关,条件编译判断是否定义了宏NULLSAFE_ENABLED

若未定义则NULLSAFE_ENABLED定义为 1

如果需要关闭这个类的功能,

NULLSAFE_ENABLED置为 0

或者在其他可以预编译的地方将 NULLSAFE_ENABLED置为 0

 

#pragma clang diagnostic ignored "-Wgnu-conditional-omitted-operand"

这个是clang编译器前端警告忽略

但是忽略的内容很奇怪

看起来像是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方法的,不知道为什么可以生成- -。

 

 

 

转载于:https://www.cnblogs.com/LPA4/p/10143515.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值