iOS Runtime学习笔记
Runtime就是运行时, 核心就是消息机制. 对OC的函数调用,是一个动态调用过程,只有在运行的时候runtime系统才能知道真正调用的哪一个函数(C语言在函数调用过程中, 编译时候就已经决定会调用哪个函数了).
可以去下载runtime的源码: Apple官方Runtime源码
iOS Runtime中实例对象和类的本质
iOS 中实例对象的本质
先摆出结论: OC是一门面向对象的编程语言, 在编译过程中, 编译器会将OC对象转化成结构体.
我们去中的objc.h
中找到:
typedef struct objc_class *Class;
typedef struct objc_object *id;
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
以上出现了我们常用的Class
, id
等关键字的定义.
我们可以看到OC中实际的类Class, 会被编译成struct objc_class
. 我们操作的类的对象实例是struct objc_object
, 并且该结构体中有一个指针指向struct objc_class
.
iOS OC中类的本质
OC对象的结构体中有一个Class
指针能够理解, 因为要知道该对象是哪个类的对象.但是我们在objc-runtime-new.h
中发现objc_class
继承自objc_object
的.
struct objc_class : objc_object {
// Class ISA; // 继承了
Class superclass;
...
在runtime.h
中, 我们看到OC类的结构体struct objc_class
的具体定义
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
因此, 两个地方都告诉我们, OC中类Class中也有一个指针指向Class
, 因此类Class本质上也是一个对象, 我们一般称为类对象, 这个指向的Class是就是元类(metaClass)的对象.
当我们调用对象方法时候, 会通过对象中的Class指针找到对应的Class,然后调用实例方法,同理当我们调用类方法时候, 会通过Class中的Class指针找到对应的meta Class,然后调用meta Class 中的方法.
OC一般会隐藏元类, 并且元类也是某个类的实例, 这个类我们一般称为根元类(root meta Class). 并且所有的元类的根元类都是一个, 并且根元类的元类是它自己. (实际中根元类是NSObject的元类)
下面是一个非常有名的图, 分表表示 isa 指针 和 super class指针.
对象-类-元类-根元类的关系用一个实例表示这个过程如下:
NSString实例的isa指针链iOS 中OC方法调用的本质
iOS方法的动态调用
我们一般将OC中的方法调用称为消息发送, 具体格式是[receiver message].例如:
NSMutableString *str = [[NSMutableString alloc] initWithString:@"hello"];
[str appendString:@" world"];
其中str
就是receiver, appendString:
就是message.
在message.h
头文件中如下方法,这个方法是runtime的核心方法,
void objc_msgSend(void /* id self, SEL op, ... */ )
objc_msgSend(receiever, selector, arg1, arg2, ...)
调用实例如下:
objc_msgSend(str, @selector(appendString:), @" world");
该消息方法为消息的动态绑定完成了以下工作:
- 它会主动查找receiver的selector对应的方法实现IMP
- 然后将参数传递给receiver object, 然后调用这IMP
- 最后返回该方法的返回值
为了使得objc_msgSend
能完成通过selector查找receiver对应的IMP, 我们在上一节中提到的OC类和对象的结构就非常重要.
通过上一节的内容,我们知道一个OC类和OC对象有会有一个isa指针,指向他们各自的Class, 同时OC类还有一个super指针指向父类.
下图非常清晰的展示了这个指针链结构. 同时圆形表示对象object, 方形表示类class. 在object's class中, 会存储 <selector, address> 的键值对, 我们一般称为dispatch_table, 方便我们通过某个object的class查找selector对应的address(IMP)
image具体过程就是通过isa指针找到对应的class struct, 然后在dispatch table里面查找selector对应的方法, 如果没有找到,那么通过super指针查找父类的dispatch table, 一直找下去, 直到NSObject类, 如果还没有找到,就调用NSObject的doesNotRecognizeSelector:
方法, 然后报unrecognized selector
错误.(实际中间还有消息转发等内容, 后面会讲到).
当然在消息查找的过程中, 会使用一个cache来加速这个过程. 其中, 对象方法(instance method)会保存到类对象(class object)的method list. 类方法(class method)会保存到meta class的 method list.
Selector, Message, Method的含义
Selector: 表示method的name. 我们一般见到的
alloc
,init
,setObject:forKey:
等等.一般用SEL表示SEL aSelector = @selector(doSomething:)
或者SEL aSelector = NSSelectorFormString(@"doSomething:")
Message: 消息是一个selector和参数一起被发送的给receiver
Method: 方法是selector和 implementation的集合, implementation是一个函数指针(IMP).
Method Signature: 表示一个method能够接受的参数类型和返回数据类型.
消息转发, unrecognized selector的补救
上一节中提到,对象通过消息这种机制查找struc objc中的方法地址.如果在整个isa/super指针链中找不到对应的selector-IMP, 那么说明该对象无法收到这个消息, 此时就会进入消息转发(message forwarding)的环节, 通过这种机制, 我们可以在消息转发的过程中告知对象如何处理该selector.
消息转发分成两个阶段: 1. 动态方法解析(dynamic method resolution), 2. 完整消息转发机制(full forwarding mechanism).
1. 动态方法解析
主要是询问receiver所属的类, 看它是否能动态添加方法, 处理该unrecognized selector
.涉及一下方法, 分别是实例方法和类方法:
+ (BOOL)resolveInstanceMethod:(SEL)selector;
+ (BOOL)resolveClassMethod:(SEL)selector;
2. 完整的消息转发机制
其实本阶段也分成两个阶段:
1> 备份者机制(replacement receiver). 或者称为重定向
2> 真正的消息转发机制.
第一种情况,如果前面阶段都没有完成. 那么runtime会请求receiver的- (id)forwardingTargetForSelector:(SEL)selector
方法,询问它是否有其他的receiver来帮助处理这个selector.
第二种情况. 此时只能启用真正的消息转发机制。完整的消息转发机制是这样的:
- 首先通过
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
返回一个NSInvocation对象, 这个方法中生成的NSInvocation作用在于把尚未处理的那条message有关的全部细节封装于这个NSInvocation对象中。此对象中包含选择子(selector)、目标(target)及参数。
- 在返回的NSInvocation对象不为nil时,
消息派发系统(message-dispatch system)
将亲自触发- (void)forwardInvocation:(NSInvocation *)anInvocation
,把message dispatch给目标对象。
其中,有以下内容需要注意:
实现此方法时,如果发现调用操作不应该由本类处理,则需要沿着继承体系,调用父类的同名方法,这样一来,继承体系中的每个类都有机会处理这个调用请求,直至rootClass,也就是NSObject类。如果最后调用了NSObject的类方法,那么该方法还会继而调用doesNotRecognizeSelector
以抛出异常(unrecognized selector send to instance xxx),此异常表明选择子最终也未能得到处理。消息转发到此结束。
网友评论