1.基础介绍
上半年看了一些关于runtime的知识点,感觉受益匪浅,而且多次用到了这些知识,包括公司的架构搭建,JSPath,Aspect,MJReferch中都用到了runtime。这篇文章算是总结了自己所掌握的知识,如果有讲的不对的地方,请大家指出。共同进步~O(∩_∩)O~
问题:1.id类型为何能指向任意的oc对象?
2.oc是动态语言,其动态是如何实现的?
3.消息转发机制是什么?有什么作用?
4.我们可以掌握runtime有什么用?
让我们带着问题看接下来的问题。
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
Class 被定义为一个指向 objc_class的结构体指针,这个结构体表示每一个类的类结构。
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
struct objc_class * isa;
struct objc_class * super_class; /*父类*/
const char *name; /*类名字*/
long version; /*版本信息*/
long info; /*类信息*/
long instance_size; /*实例大小*/
struct objc_ivar_list *ivars; /*实例参数链表*/
struct objc_method_list **methodLists; /*方法链表*/
struct objc_cache *cache; /*方法缓存*/
struct objc_protocol_list *protocols; /*协议链表*/
Class 是指向类结构体的指针,该类结构体含有一个指向其父类类结构的指针,该类方法的链表,该类方法的缓存以及其他必要信息。
NSObject 的class 方法就返回这样一个指向其类结构的指针。每一个类实例对象的第一个实例变量是一个指向该对象的类结构的指针,叫做isa。
通过该指针,对象可以访问它对应的类以及相应的父类。
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
objc_object中 只有Class isa 指针,凡是拥有isa指针的,都是oc对象,即id对象。所有继承于NSObject的对象,都会包含isa指针。所以,id可以指向任意的继承于NSObject的类和对象。
二 元类
第一次接触到元类,觉得这样的思想很有意思。
UIView *view = [[UIView alloc]]init];
实例对象view 其isa指针指向UIView. 其中objc_object 里,有对象方法的方法列表(methodLists),属性(ivars),方法缓存(cache),父类(super_class)等信息,换句话说,其实view对象的isa指针,指向的是UIView的objc_object结构体,里面包含了view所有的信息。
那类方法放在哪里呢?
[UIView animateWithDuration:1 animations:^{ }]; 我们经常会调用这个方法实现动画,animateWithDuration就是个类方法。
UIView的isa指针,指向UIView的元类。这个UIView 称谓元类的类对象。这个元类中,包含了UIView所有的信息。
三方法调用
这个图很巧妙,相信大家已经看过很多边了。其顺序是:
Instance of Subclass ->Subclass(class)->Superclass(class)->Rootclass(class)->nil. 这个是对象调用方法的顺序。
Subclass(class)->Subclass(meta)->Superclass(meta)->Rootclass(meta)-->self 这个是类调用方法的顺序。
你看懂了吗?
四 方法调用细则和消息转发机制
Bird * aBird = [[Bird alloc] init];
[aBird fly];
消息直到运行时才绑定到方法实现上。编译器会将消息表达式[receiver message]转化为一个消息函数的调用,即objc_msgSend。
objc_msgSend(receiver, selector, arg1, arg2, ...)
1.objc_msgSend
找到SEL对应的IMP实现。通过消息接收者的类型。
查找IMP的具体流程:
当消息发给一个对象时,objc_msgSend通过对象的isa指针获取到类的结构体,然后在方法分发表里面查找方法的selector。如果没有找到selector,则通过objc_msgSend结构体中的指向父类的指针找到其父类,并在父类的分发表里面查找方法的selector。依此,会一直沿着类的继承体系到达NSObject类。
如果没有找到,走消息转发机制。
1.动态方法解析
+resolveInstanceMethod:
通过class_addMethod函数动态添加到类里面
2.备用接收者
- (id)forwardingTargetForSelector:(SEL)aSelector
3.完整转发
- (void)forwardInvocation:(NSInvocation *)anInvocation
2.然后将消息接收者以及方法中指定的参数信息传递给方法实现IMP。
3.将方法实现的返回值作为函数的返回值返回。
六 总结
1.isa 指向的是代表类型 super_class 是代表的继承关系。 metaclass 的 isa 指向根 metaclass,如果该 metaclass 是根 metaclass 则指向自身;metaclass 的 super_class 指向父 metaclass。如果是NSObject 的metaclass 则(super_class)指向NSObject。
2.Class 被定义为一个指向 objc_class 的结构体指针,这个结构体表示每一个类的类结构。NSObject 的 class 方法就返回这样一个指向其类结构的指针。
每一个类实例对象的第一个实例变量是一个指向该对象的类结构的指针,叫做 isa。通过该指针,对象可以访问它对应的类以及相应的父类。
3.NSObject 类中的 methodForSelector:方法就是这样一个获取指向方法实现 IMP 的指针,
methodForSelector:来避免动态绑定将减少大部分消息的开销,但是这只有在指定的消息被重复发 送很多次时才有意义,例如上面的 for 循环。
我们可以通过 NSObject 的一些方法获取运行时信息或动态执行一些消息:
4.class 返回对象的类;
isKindOfClass 和 isMemberOfClass 检查对象是否在指定的类继承体系中; respondsToSelector 检查对象能否相应指定的消息;
conformsToProtocol 检查对象是否实现了指定协议类的方法;
methodForSelector 返回指定方法实现的地址。
performSelector:withObject 执行 SEL 所指代的方法。
5.forwardInvocation:
当一个对象由于没有相应的方法实现而无法响应某消息时,运行时系统将通过 forwardInvocation:消息通知该对象。每个对象都从NSObject 类中继承了 forwardInvocation:方法。
要转发消息给其它对象,forwardInvocation:方法所必须做的有:
1,决定将消息转发给谁,并且
2,将消息和原来的参数一块转发出去。
- (void) forwardInvocation:(NSInvocation *)anInvocation {
if ([someOtherObject respondsToSelector:[anInvocation selector]]) [anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
forwardInvocation:方法就像一个不能识别的消息的分发中心,将这些消息转发给不同接收对象。或者它也 可以象一个运输站将所有的消息都发送给同一个接收对象。它可以将一个消息翻译成另外一个消息,或者 简单的"吃掉―某些消息,因此没有响应也没有错误。forwardInvocation:方法也可以对不同的消息?供同样 的响应,这一切都取决于方法的具体实现。该方法所?供是将不同的对象链接到消息链的能力