OC中nil是一个指向不存在的对象指针,OC中的对象定义默认赋值为nil,而数组和字典里是不可以有nil的,但可以为[NSNull null];
另外在框架层面,Foundation定义了NSNull,只有一个类方法+null,它返回一个单独的NSNull对象。
NSNull与nil以及NULL不同,因为它是一个实际的对象,而不是一个零值。
OC方法调用流程
在此之前先说一下OC中的类在runtime中是如何表示的。
信息构成由上面的代码可知,一个类由以上的一些信息构成,有了这些信息OC方法的调用就可以正常运行起来。
OC方法的调用实际上就是发送消息objc_send(id, SEL, ...),它首先会在对象的类对象的cache,methodlist以及父类对象的 cache,methodlist中依次查找SEL对应的IMP,如果没有找到且实现了动态方法决议机制就会进行决议,如果没有实现动态方法决议机制或决议失败且实现了消息转发机制就会进入消息转发流程,否则程序 crash 。也就是说如果同时提供了动态方法决议和消息转发,那么动态方法决议先于消息转发,只有当动态方法决议依然无法正确决议selector 的实现,才会尝试进行消息转发。流程图如下:
图一
转发图二
执行流程1. 动态方法决议(Method Resolution)
OC提供了一种名为动态方法决议的手段,使得我们可以在运行时动态地为一个selector提供实现。我们只要实现+resolveInstanceMethod:或+resolveClassMethod:方法,并在其中为指定的selector提供实现即可(通过调用运行时函数class_addMethod来添加)。这两个方法都是NSObject中的类方法,其原型为:
方法参数name是需要被动态决议的selector;返回值文档中说是表示动态决议成功与否。在不涉及消息转发的情况下,如果在该函数内为指定的selector提供实现,无论返回 YES 还是 NO ,编译运行都是正确的;但如果在该函数内并不真正为selector提供实现,无论返回 YES 还是 NO,运行都会 crash,道理很简单,selector并没有对应的实现,而又没有实现消息转发。resolveInstanceMethod是为对象方法进行决议,而 resolveClassMethod是为类方法进行决议。
2. 消息转发(Message Forwarding)
Fast Forwarding
如果目标对象实现- forwardingTargetForSelector:方法,系统就会在运行时调用这个方法,只要这个方法返回的不是nil或self,也会重启消息发送的过程,把这消息转发给其他对象来处理。否则,就会继续Normal Fowarding。
Normal Forwarding
如果没有使用Fast Forwarding来消息转发,最后只有使用Normal Forwarding来进行消息转发。它首先调用methodSignatureForSelector:方法来获取函数的参数和返回值,如果返回为nil,程序会 crash 掉,并抛出unrecognized selector sent to instance异常信息。如果返回一个函数签名,系统就会创建一个NSInvocation对象并调用-forwardInvocation:方法。
3. 使用场景
在一个函数找不到时,Objective-C提供了三种方式去补救:
1. 调用resolveInstanceMethod给个机会让类添加这个函数实现
2. 调用forwardingTargetForSelector 让别的对象去执行这个函数
3. 调用methodSignatureForSelector(函数符号制造器)和forwardInvocation(函数执行器)灵活的将目标函数以其他形式执行。
4. 如果都不成功,调用doesNotRecognizeSelector抛出异常。
NullSafe
对查找不到的方法进行最后的拦截处理,遍历所有的类看有没有类中的实例实现了此方法,如果没有有就返回对应的函数签名并通过forwardInvocation: 将其执行函数的对象变为nil去执行,这样就不会引起崩溃了。
处理方法这样处理也不是不可以,但可能会造成在开发过程中出现没有实现该方法然而在运行方法调用的时候Xcode也不会崩溃,从而导致开发者在开发过程中较难定位问题。
-(void)doesNotRecognizeSelector:(SEL)aSelector方法为什么我们不能通过给NSObject创建一个 category,重写这个方法,在这里处理消息未被处理的情况呀?
1. 在苹果的官方文档中,明确提到,“一定不能让这个函数就这么结束掉,必须抛出异常”。(不调用super实现)
2. 在分类重写该方法处理各种消息未被处理的情况,会让这个分类的方法特别长,不利于维护。
参考:
网友评论