#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的应用有必定启发。