Objective-C扩展了C语言,并加入了面向对象特性和消息机制.OC中最核心的部分是运行时库,是由C和编译语言写的Runtime库.OC是一门动态语言,不仅需要编译器来编写代码,同时也需要一个运行时系统来动态创建类和对象,进行消息传递和转发.了解Runtime机制,就要先了解他的消息传递.
事实上,我们编写的Objective-C方法调用的语法,最终会被翻译成一个C的函数调用-objc_megSend().
[array insertObject:object atIndex:4];
objc_msgSend(array, @selector(insertObject: atIndex:),object,4);
消息传递的关键在于objc_msgSend中的isa指针和objc_class中的class dispatch table中
我们来看一下objc_object ,objc_class以及objc_method
objc_object objc_class obcj_method_list objc_method我们可以看到objc_method_list其实是一个objc_method构成的可变长度的数组.而且每一个method_name表示方法名SEL,还有一个表示函数类型的c字符串,还有就是一个方法的实现IMP.
从上面的总结中我们可以得出objc_msgSend(array, @selector(insertObject: atIndex:),object,4);的函数调用过程是这样的,先通过isa指针找到class,再通过class找到method_list中找到insertObject: atIndex:这个方法,在class中没找到这个方法,就会去superClass中找,一直找到NSObject.一旦找到这个方法,就回去执行他的IMP方法实现.
但是如果这个方法被不停的调用,而每次都是通过这样的过程去查找,就会很浪费效率,所以在objc_class中我们看到了objc_cache,当我们首次调用insertObject: atIndex:时,系统会将这个的方法名作为key,IMP作为值保存到cache中,当后面调用时,直接查找cache便能快速调用方法,大大提高了效率.
动态方法解析和转发
举一个例子:我在一个类中申明了这个方法,但是却没有实现这个方法.如果一旦调用就会产生崩溃,抛出抛出unrecognized selector sent to …的异常,但是系统为了避免这种情况的发生给我们提供了三次修改他的机会:
1.Method resolution
2.Fast forwarding
3.Normal forwarding
首先Objective-C运行时会调用+resolveInstanceMethod::或者+resolveClassMethod:,让你有机会提供方法实现的机会,如果你添加了方法的实现,并且返回yes,那么运行时系统就会重新启动一次消息转发的过程.
例如
Method resolution我申明了parseChapterData这个方法,但却没有实现,最终导致崩溃,但是我通过上面的方法,给这个方法提供一个方法实现,并且返回了YES.就可以避免崩溃.iOS系统中CoreData运用这方面的比较多.但是如果上面的方法返回为NO,那么运行时就会进行消息转发.
imp_implementationWithBlock()
可以通过block快速的为这个方法创建实现.
message forwardingFast forwarding
还有两种解决方案就是,如果这个目标对象实现了-forwardingTargetForSelector:,运行时系统就会有机会将这个消息转发给其他对象的机会.只要这个方法的返回值不是self或者是nil,那么这个消息发送就会重启.当然发送的对象就是你返回的那个对象.
Normal forwarding
这是最后一次解救你这个程序不会崩溃的机会了,首先他会发送-methodSignatureForSelector:,消息发送函数的参数和返回值类型,如果这个函数返回nil.运行时会发送-doesNotRecognizeSelector:消息,程序这时也就挂掉了.
如果返回了一个函数签名,Runtime 就会创建一个 NSInvocation 对象并发送-forwardInvocation:消息给目标对象。
NSInvocation 实际上就是对一个消息的描述,包括selector 以及参数等信息。所以你可以在-forwardInvocation:里修改传进来的 NSInvocation 对象,然后发送-invokeWithTarget:消息给它,传进去一个新的目标
总结
1.会通过isa指针找到class,在class的method_list中查找方法,找到后就去执行方法实现,如果没找到就去superClass查找.如果还没有找到就会抛出异常.
2.如果没有找到运行时系统会尝试+resolveInstanceMethod或者+resolveClassMethod方法来处理这个消息.
3.如果上面的方法返回NO,则消息会进入下一步操作-forwardingTargetForSelector,将消息允许转发给其他的对象.但是也要保证这个对象实现了这个方法.
4.如果没有其他对象处理你这条消息,那么就剩最后一条机会来处理这个消息了.运行时系统会调用-methodSignatureForSelector:和-forwardInvocation:消息.你可以手动通过-invokeWithTarget:这个来转发消息给一个对象,或者通过-doesNotRecognizeSelector:来抛出异常.
学习OC的基础就是了解他的消息机制,他是如何进行消息传递的,而且他为了保证方法的执行效率对执行过的方法进行了缓存.提高方法的查找效率.而且运行时库允许我们在出现崩溃时候,挽救我们的程序.
谢谢
本人联系方式:qq:513961360
email:513961360@qq.com
也可以加我们的qq群希望能与朋友们一起聊天和学习.群里还有很多iOS开发者,帮助我们解决问题,并且同时学习.
qq群号:580284575
网友评论