A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 602516169 中级黑马   /  2016-8-27 15:39  /  1356 人查看  /  1 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

iOS runtime机制
字数1860 阅读3854 评论1 喜欢12
本文详细整理了 Cocoa 的 Runtime 系统的知识,它使得 Objective-C 如虎添翼,具备了灵活的动态特性,使这门古老的语言焕发生机。主要内容如下:

引言

简介

与 Runtime 交互

Rumtime 术语

消息

动态方法解析

消息转发

健壮的实例变量(Non Fragile ivars)

Objective-C Associated Objects

Method Swizzling

总结

本文为转载,出处链接后续补上。。

引言

曾经觉得Objc特变方便上手,面对着Cocoa中大量API,只知道简单地查看文档和调用。还记得初学 Objective-C 时把[receiver message]当成简单地方法调用,而无视了“发送消息”这句话的深刻含义。于是[receiver message]会被编译器转化为:

objc_mesgSend(receiver,selector)

如果消息含有参数,则为:

objc_mesgSend(receiver, selector, arg1, arg2, ...)

如果消息的接收者能够找到对应的selector,那么就相当于直接执行了接收者这个对象的特定方法;否则,消息要么被转发,或是临时向接收者动态添加这个selector对应的实现内容,要么就干脆玩完奔溃掉。

现在可以看出[receiver message]真的不是一个简简单单的方法调用。因为这只是在编译阶段确定了要向接收者发送message这条消息,而receive将要如何响应这条消息,那就要看运行时发生的情况来决定了。

Objective-C 的 Runtime 铸就了它动态语言的特性, 这些深层次的只是虽然平时写代码用的少一些,但是却是每个Objc程序员需要了解的。

简介

因为Objc是一门动态语言,所以它总是想办法把一些决定工作从编译链接推迟到运行时。也就是说只有编译器是不够的,还需要一个运行时系统(runtime system)来执行编译后的代码。这就是 Objective-C Runtime 系统存在的意义,它是整个Objc运行框架的一块基石。

Runtime 其实有两个版本:"modern"和"legacy"。我们现在用的 Objective-C 2.0 采用的是现行(Modern)版的 Runtime 系统,只能运行在 iOS 和 OS X 10.5 之后的64位程序中。而OS X较老的32位程序仍采用 Objective-C 1中得(早期) Legacy 版本的 Runtime 系统。这两个版本最大的区别在于当你更改一个类的实例变量的布局时,在早期版本中你需要重新编译它的子类,而现行版本就不需要。

Runtime 基本使用C和汇编写的,可见苹果为了动态系统的高效而做出的努力。你可以在这里下载到苹果维护的开源代码。苹果和GNU各自维护一个开源的runtime版本,这两个版本之间都在努力的保持一致。

与Runtime交互

Objc 从三种不同的层级上与 Runtime 系统进行交互,分别是通过 Objective-C 源代码,通过 Foundation 框架的NSObject类定义的方法,通过对runtime函数直接调用。

Objective-C源代码

大部分情况下你就只管写你的Objc代码就行,runtime系统自动在幕后辛勤劳作着。

还记得引言中举的例子吧,消息的执行会使用到一些编译器为实现动态语言特性而创建的数据结构和函数,Objc中得类、方法和协议等在 runtime 中都由一些数据结构来定义,这些内容在后面会讲到。(比如objc_msgSend函数及其参数列表中得id和SEL都是啥 )

NSObject的方法

Cocoa中大多数类都继承于NSObject类,也就自然继承了它的方法。最特殊的例外是NSProxy,它是个抽象超类,它实现了一些消息转发有关的方法,可以通过继承它来实现一个其他类的替身类或是虚拟出一个不存在的类,说白了就是领导把自己展现给大家风光无限,但是把活都交给幕后小弟去干。

有的NSObject中得方法起到了抽象接口的作用,比如description方法需要你重载它并为你定义的类提供描述内容。NSObject还有些方法能在运行时获得类的信息,并检查一些特性,比如class返回对象的类;isKindOfClass: 和 isMemberOfClass: 则检查对象是否在指定的类继承体系中;respondsToSelector:检查对象能否响应指定的消息;conformsToProtocol:检查对象是否实现了指定协议类的方法;methodForSelector:则返回指定方法实现的地址。

Runtime的函数

Runtime 系统是一个由一系列函数和数据结构组成,具有公共接口的动态共享库。头文件存放于/usr/include/objc目录下。许多函数允许你用纯C代码来重复实现 Objc 中同样地功能。虽然有一些方法构成了NSObject类的基础,但是你在写 Objc 代码时一般不会直接用到这些函数的,除非是写一些 Objc 与其他语言的桥接或是底层的debug工作。在Objective-C Runtime Reference中有对 Runtime 函数的详细文档。

Runtime术语

还记得引言中得objc_msgSend:方法吧,它的真身是这样的:

id objc_msgSend(id self, SEL op, ...);

下面将会逐渐展开介绍一些术语,其实它们都对应着数据结构。

SEL

objc_msgSend函数第二个参数类型为SEL,它是selector在Objc中的表示类型(Swift中是Selector类)。selector是方法选择器,可以理解为区分方法的ID,而这个ID的数据结构是SEL:

typedef struct objc_selector *SEL;

其实它就是个映射到方法的C字符串,你可以用Objc编译器命令@selector()或者 Runtime 系统的sel_registerName函数来获得一个SEL类型的方法选择器。

不同类中相同名字的方法所对应的方法选择器是相同的,即使方法名字相同而变量类型不同也会导致它们具有相同的方法选择器,于是 Objc 中方法命名有时会带上参数类型(NSNumber一堆抽象工厂方法拿走不谢),Cocoa中有好多长长的方法哦。

id

objc_msgSend第一个参数类型为id,大家对它都不陌生,它是一个指向类实例的指针:

typedef struc objc_object *id;

那objc_object又是啥呢:

struct objc_object { Class isa;};

objc_object结构体包含一个isa指针就可以顺藤摸瓜找到对象所属的类。

Class

之所以说isa是指针是因为Class其实是一个指向objc_class结构体的指针:

typedef struct objc_class *Class;

而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;

可以看到运行时一个类还关联了它的超类指针,类名,成员变量,方法,缓存,还有附属的协议。

其中objc_ivar_list和objc_method_list分别是成员变量列表和方法列表:

struct objc_ivar_list {

int ivar_count                                          OBJC2_UNAVAILABLE;

#ifdef __LP64__

int space                                                OBJC2_UNAVAILABLE;

#endif

/* variable length structure */

struct objc_ivar ivar_list[1]                            OBJC2_UNAVAILABLE;

}                                                            OBJC2_UNAVAILABLE;

struct objc_method_list {

struct objc_method_list *obsolete                        OBJC2_UNAVAILABLE;

int method_count                                        OBJC2_UNAVAILABLE;

#ifdef __LP64__

int space                                                OBJC2_UNAVAILABLE;

#endif

/* variable length structure */

struct objc_method method_list[1]                        OBJC2_UNAVAILABLE;

}

1 个回复

倒序浏览
小白水平有限,表示没看懂
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马