OC-runtime学习札记

前言

结论:ObjC是一种面向对象的编程语言,具有运行时的特性,是通过ObjC runtime来实现的,即尽可能把代码执行的决策从编译链接推迟到运行时。

与静态语言相比一个明显的例子:

#include < stdio.h >

int main(int argc, const char **argv[])
{
        printf("Hello World!");
        return 0;
} 

编译后得到相应的汇编语言,再链接库,生成可执行代码。

对于ObjC中常见的消息发送:

[self doSomethingWithVar:var1];
编译器编译后得到:
objc_msgSend(self,@selector(doSomethingWithVar:),var1);
得到一个C的方法,传入了三个变量,仅此。

对于ObjC runtime来说,需要做的事就是加载类的信息,进行方法的分发和转发等操作。

接下来看一些基本的概念:

1、id的定义

对象结构体:

typedef struct objc_object {
    Class isa;
} *id;

任意类型(id)是一个objc_object结构类型的指针,所有的objc_object对象的结构体中都有一个isa指针,这个isa指向它所属的类,

运行时靠这个isa指针来检测这个对象是否可以响应一个selector。

2、Class的定义:

类结构体:

typedef struct objc_class *Class;

Class 是一个objc_class结构类型的指针;

接下来看objc_class的定义:

struct objc_class
{
    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;
};

解读isa

发现有两个isa,OC对类对象和实例对象中的isa所指向的类结构作了不同的命名:

类对象中的isa指向类结构被称作meta class,meta class存储类的static类成员变量与+开头的static类成员方法。

实例对象中的isa指向类结构被称作 class(就是平时所得该对象所属的类),class结构存储类的普通成员变量与-开头的普通成员方法。

解读super_class

顾名思义,super_class指向当前类的父类,特殊的是根类NSObject的super_class指向nil。

关于类 父类 根类 ,子类的isa 类的isa关系总结:

1、类的实例对象的isa指向该类,该类的isa指向该类的meta class;

2、类的super_class指向该类的父类,根类的super_class指向nil;

3、meta class 的isa 指向根类的meta class,根类的meta class 指向根类meta class自身。

4、meta class 的super_class指向父类的meta class,根类的meta class的super_class指向meta class 对应的类。

转载一张图,清晰明了:

专门说一下,class与meta class的区别:

class 是 实例对象的类型,向 实例对象 发送消息(调用实例方法)时,我们在该实例对象的class结构的methodlists(Class结构体中查看)中查找响应的函数,

如果没有找到则到该class的父类的methodlists中去查找,就这样一直找到跟类的methodlists,仍没有找到就会响应动态方法解析与消息转发机制

meta class 是 类对象的类型,向 类对象 发送消息(调用类方法)时,我们在该类对象的meta class结构的methodlists中查找响应的函数,

如果没有找到则到该meta class的父类的methodlists中去查找,就这样一直找到跟类的methodlists,仍没有找到就会响应动态方法解析与消息转发机制。

name

一个c字符串,只是类的名称,可以通过这个名称查找到该类。

通过:id objc_getClass(const char *aClassName))或该类的 metaclass(id objc_getMetaClass(const char *aClassName)

instance_size

该类的实例变量大小包括父类继承下的。

ivars

指向 objc_ivar_list 的指针,存储每个实例变量的内存地址,如果该类没有任何实例变量则为 NULL。

实例变量是通过基类地址加变量位置偏移得到的。

info

供运行期使用的一些位标识。

methodLists

与info的一些标志位有关,如果 info 设置了 CLS_CLASS 则 objc_method_list  存储实例方法,如果设置的是 CLS_META 则存储类方法。

cache:

指向 objc_cache 的指针,用来缓存最近使用的方法,以提高效率。

protocol

指向 objc_protocol_list 的指针,存储该类声明要遵守的正式协议。

3、objc_msgSend定义

id objc_msgSend(id self, SEL op, ...)
typedef struct objc_selector *SEL;
objc_selector是一个根据方法的名字来区分方法的唯一ID,只要名字相同selector就相同。

不同的类可以拥有相同的selector,不同类的实例对象执行相同的selector时,会在各自的方法列表中根据selector去寻找自己对应的IMP。


4、IMP定义

typedef id (*IMP)(id, SEL, ...); 
IMP本质就是一个函数指针,这个被指向的函数包含一个接收消息的对象id,调用方法的SEL,以及一些方法参数,并返回一个id。

因此我们可以通过SEL获得它所对应的IMP,在取得了函数指针之后,也就意味着我们取得了需要执行方法的代码入口,这样我们就可以像普通的C语言函数调用一样使用这个函数指针。


5、msg_sender工作流程

在Objective-C中,消息直到运行时才会绑定到方法的实现上。编译器会把代码中[target doSth]转换成objc_msgSend消息函数,这个函数完成了动态绑定的所有事情。它的运行流程如下:
  1. 检查selector是否需要忽略。(ps: Mac开发中开启GC就会忽略retain,release方法。)
  2. 检查target是否为nil。如果为nil,直接cleanup,然后return。(这就是我们可以向nil发送消息的原因。)
  3. 然后在target的Class中根据Selector去找IMP

寻找IMP的过程:

  1. 先从当前class的cache方法列表(cache methodLists)里去找
  2. 找到了,跳到对应函数实现
  3. 没找到,就从class的方法列表(methodLists)里找
  4. 还找不到,就到super class的方法列表里找,直到找到基类(NSObject)为止
  5. 最后再找不到,就会进入动态方法解析和消息转发的机制。

objc_method_list存储当前类的方法链表,里面包含objc_method,它存储了类的某个方法的信息。
typedef struct objc_method *Method;
struct objc_method {
    SEL method_name                                          OBJC2_UNAVAILABLE;
    char *method_types                                       OBJC2_UNAVAILABLE;
    IMP method_imp                                           OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;

结论,objct_class中的method_list保存了一组SEL<->IMP的关系映射表。

6、动态方法解析和消息转发机制


当在最终的MethodLists里仍找不到响应方法时,系统提供三次机会,否则抛出异常,程序崩溃。

① 动态方法解析 +(Bool)resolveInstanceMethod:(SEL)sel

举例:通过sel判断满足条件后,增加一个响应方法,并返回YES

<pre name="code" class="objc">+(Bool)resolveInstanceMethod:(SEL)sel
{
     if(sel == @selector(method:)){
       class_addMethod([self class],sel,(IMP)newMethod,"v@:");
       
     }
     <code class="js spaces"></code><code class="js keyword">return</code> <code class="js plain">[</code><code class="js keyword">super</code> <code class="js plain">resolveInstanceMethod:sel];</code>
 }
备用接受者 - (id)forwardingTargetForSelector:(SEL)aSelector;

返回一个能响应该方法的target对象
③ 消息转发
- (void)forwardInvocation:(NSInvocation *)anInvocation
对象会创建一个表示消息的NSInvocation对象,把与尚未处理的消息有关的全部细节都封装在anInvocation中,包括selector,目标(target)和参数。
我们可以在forwardInvocation方法中选择将消息转发给其它对象。

我们必须重写以下方法:
1
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector








                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值