iOS Runtime学习笔记(一) - 基础学习

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.

第二种情况. 此时只能启用真正的消息转发机制。完整的消息转发机制是这样的:

  1. 首先通过-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector返回一个NSInvocation对象, 这个方法中生成的NSInvocation作用在于把尚未处理的那条message有关的全部细节封装于这个NSInvocation对象中。此对象中包含选择子(selector)、目标(target)及参数。
  1. 在返回的NSInvocation对象不为nil时,消息派发系统(message-dispatch system)将亲自触发- (void)forwardInvocation:(NSInvocation *)anInvocation,把message dispatch给目标对象。

其中,有以下内容需要注意:

实现此方法时,如果发现调用操作不应该由本类处理,则需要沿着继承体系,调用父类的同名方法,这样一来,继承体系中的每个类都有机会处理这个调用请求,直至rootClass,也就是NSObject类。如果最后调用了NSObject的类方法,那么该方法还会继而调用doesNotRecognizeSelector以抛出异常(unrecognized selector send to instance xxx),此异常表明选择子最终也未能得到处理。消息转发到此结束。

具体的流程如下

参考文献

Apple官方Runtime开源源码

iOS runtime探究(一): 从runtime开始理解面向对象的类到面向过程的结构体

selector/Message/Method的关系

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354

推荐阅读更多精彩内容