美文网首页
NullSafe 学习

NullSafe 学习

作者: QuakOrigin | 来源:发表于2020-01-10 17:37 被阅读0次

    作为处理向 NSNull 发送消息导致的崩溃处理框架,NullSafe 应该是最轻量简洁的了,算起来 50行 代码不算就解决了开发中的NSNull异常的棘手问题。

    问题的出现于处理基于Objective-C语言的特性:

    • OC 中方法调用其实就是向对象发送消息
    • 发送出去的消息必须被处理
    1. NSNull 添加 Category, 这样就无需用代码引入,系统检测到对 NSNull 发送任何消息都会自动调用 NullSafe 类,如果不想使用,可以用开关关掉
    #define NULLSAFE_ENABLED 0
    
    1. 接一个 Xcode 警告⚠️消除
    #pragma clang diagnostic ignored "-Wgnu-conditional-omitted-operand"
    

    看解释是消除代码中使用了类似三元运算符?:而没有添加中间变量的warning,但细看代码,并没有类似的逻辑。
    其他关联 ignored 可以参考 使用 #pragma 阻止一些warnings

    1. iOS 中对方法的调用,其实就是向目标对象发送各种消息, 造成程序崩溃的主要原因就是因为目标对象无法处理接收到的消息,最后抛出 doseNotRecognizeSelector 异常。结合 runtime,在目标对象无法处理接收到的消息的时候,还有补救措施,例如 Fast ForwardNormal Forward, NullSafe 正是利用了消息转发过程的逻辑,跳过 动态方法解析获取备用接收者 的过程,直接处理消息转发的最后一步。这一步的关键就是重写
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
    

    这个方法,从而获取到 NSMethodSignature 对象,只要获取到的 signature != nil 就可以使用备用调用者来处理此消息。

    - (void)forwardInvocation:(NSInvocation *)invocation
    

    这一步处理的目的是对消息进行处理,也就是文章开头提到的第二点,所有发送出去的消息必须要处理才可以。

    1. 把新消息对象设置为 nil 因为对 nil 发送任何消息都是可以的,这里就把完成了对消息的处理,而不会造成程序崩溃。

    2. 获取处理该消息的新类都是 NSObject 的子类,如果有可变对象,则用可变对象,若没有则用本对象。

    for (Class someClass in @[
                [NSMutableArray class],
                [NSMutableDictionary class],
                [NSMutableString class],
                [NSNumber class],
                [NSDate class],
                [NSData class]
            ])
    

    一般来说

    MutableClass : Class
    

    这样对于同一个类型的类来说,只需要 MutableClass 即可,如果 MutableClass 无法处理此消息,那么 Class 也无法处理。

    1. 省去了 .h 文件,最大化的让代码变轻
      这种方式倒是不常见,为此自己仿写一个实验下效果,
    • 建立一个 NSString+PrintCategory;
    • 删掉 .h 文件;
    • 删掉 .m 中的 #import "NSString+Print.h"
    • 加入类库引用 #import <Foundation/Foundation.h>
    • 随便添加个类方法和实例方法
      完整代码(NSString+Print.m)
    #import <Foundation/Foundation.h>
    
    @implementation NSString (Print)
    
    + (void)print:(NSString *)str {
        NSLog(@"😃 %@", str);
    }
    
    - (void)print:(NSString *)str {
        NSLog(@"😁 %@", str);
    }
    
    @end
    

    在其他文件中测试该方法,由于NSString 和 父类 NSObject 中都不含有

    - (void) print;  //不含有此实例方法
    + (void) print; //不含有此类方法
    

    所以添加的方法只能在运行阶段识别,所以在编译时期无法识别该方法,导致编译阶段无法通过


    借助 msg_send 函数,手动发送此消息验证

    ((void (*)(id, SEL, id))objc_msgSend)([NSString class], @selector(print:), str);
    

    当然此时会报 Undeclared selector 'print:'warning 不过不影响编译.

    最后可以测试通过


    可见在无需与其他文件进行交互的前提下,单独的 .m 文件是完全可行的。

    相关文章

      网友评论

          本文标题:NullSafe 学习

          本文链接:https://www.haomeiwen.com/subject/cnopactx.html