NullSafe 学习

作为处理向 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 文件是完全可行的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 前言:面试笔试都是必考语法知识点。请认真复习和深入研究OC。 目录:iOS-面试题-OC基础篇 (1) - (84...
    麦穗0615阅读 4,276评论 0 33
  • 点击查看原文 Web SDK 开发手册 SDK 概述 网易云信 SDK 为 Web 应用提供一个完善的 IM 系统...
    layjoy阅读 13,946评论 0 15
  • 一、深复制和浅复制的区别? 1、浅复制:只是复制了指向对象的指针,即两个指针指向同一块内存单元!而不复制指向对象的...
    iOS_Alex阅读 1,441评论 1 27
  • 张海萍 焦点网络中级八期 山西晋中 坚持分享第332天 2019年1月20日 周日 何为讲师?我想讲师首先是学习...
    小海儿2阅读 1,076评论 0 1
  • FutureTask介绍FutureTask是一种可以取消的异步的计算任务。它的计算是通过Callable实现的,...
    先生zeng阅读 174评论 0 1