runtime机制基础

来源:互联网 发布:陈星网络情缘酷我音乐 编辑:程序博客网 时间:2024/05/22 00:55
Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理。这种动态语言的优势在于:我们写代码时能够更具灵活性。但是runtime会在一定程度影响到代码的结构,在平时尽量减少使用runtime机制进行编码;runtime应用的时机:(1)当需要非常高的性能开发时,使用runtime,注释:oc的代码已经无法满足性能需求; (2)当我们对系统内部的实现很好奇的时候,可以用clang反编译成c++去看底层的实现机制。动态语言的特性使得oc不仅需要一个编译器,还需要一个运行时系统来执行编译的代码,这个运行时系统即runtime。runtime其实是一个runtime库,它基本上是用C和汇编写的,这个库使得C语言有了面向对象的能力。

runtime库主要做下面几件事:
(1)封装:在这个库中,对象可以用C语言中的结构体表示,而方法可以用C函数来实现,另外再加上了一些额外的特性。这些结构体和函数被runtime函数封装后,我们就可以在程序运行时创建,检查,修改类、对象和它们的方法了。
(2)找出方法的最终执行代码:当程序执行[object doSomething]时,会向消息接收者(object)发送一条消息(doSomething),runtime会根据消息接收者是否能响应该消息而做出不同的反应。

一、OC的类与对象

1.1 类的结构

在OC中类是一个class类型,在本质上是一个指向objc_class结构体的指针,定义是:typedef struct objc_class *Class;

Class isa // isa指针// 一下成员变量包含在oc2.0以上版本中Class super_class  // 父类const char *name   // 类名long version       // 类的版本信息,默认为0long info        // 类信息,供运行期使用的一些位标识long instance_size  // 该类的实例变量大小struct objc_ivar_list *ivars // 该类的成员变量链表struct objc_method_list **methodLists   // 方法定义的链表struct objc_cache *cache  // 方法缓存

在这个定义中,有几个字段需要特别关注:
1> 类自身也是一个对象,这个对象的Class里面也有一个isa指针,它指向metaClass(元类)。
2> super_class:指向该类的父类,如果该类已经是最顶层的根类(如NSObject或NSProxy),则super_class为NULL。
3> cache用于缓存最近使用的方法。一个接收者对象接收到一个消息时,它会根据isa指针去查找能够响应这个消息的对象。在实际使用中,这个对象只有一部分方法是常用的,很多方法其实很少用或者根本用不上。在我们每次调用过一个方法后,这个方法就会被缓存到cache列表中,下次调用的时候runtime就会优先去cache中查找,如果cache没有,才去methodLists中查找方法。

1.2 objc_object 和 id

objc_object是一个类的实例的结构体,id是一个objc_object类型指针定义如下:
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
typedef struct objc_object *id;
objc_object结构体只有一个字段,即指向其类的isa指针。当一个oc对象受到消息时,运行时库会根据实例对象的isa指针找到这个实例对象所属的类,之后在类或父类的方法列表中去寻找与消息对应的selector指向的方法,找到后即运行这个方法。当创建一个特定类的实例对象时,分配的内存包含一个objc_object数据结构,然后是类的实例变量的数据。NSObject类的alloc和allocWithZone:方法使用函数class_createInstance来创建objc_object数据结构。
id相当于C语言中的泛型指针void*。

1.3 objc_cache

objc_class中的方法缓存字段cache是一个指向objc_cache结构体的指针,定义如下
struct objc_cache {
unsigned int mask /* total = mask + 1 */ ;
unsigned int occupied ;
Method buckets[1] ;
};
mask:一个整数,指定分配的缓存bucket的总数。在方法查找过程中,Objective-C runtime使用这个字段来确定开始线性查找数组的索引位置。指向方法selector的指针与该字段做一个AND位操作(index = (mask & selector))。这可以作为一个简单的hash散列算法。
occupied:一个整数,指定实际占用的缓存bucket的总数。
buckets:指向Method数据结构指针的数组。这个数组可能包含不超过mask+1个元素。需要注意的是,指针可能是NULL,表示这个缓存bucket没有被占用,另外被占用的bucket可能是不连续的。这个数组可能会随着时间而增长。

1.4 metal class 元类

类本身就是一个对象,本质上也是一个objc_object类型的指针,包含一个isa指针;为了调用类方法,这个类的isa指针必须指向一个包含这些类方法的一个objc_class结构体,也就是metal class(元类:类所属的类)。
meta-class存储着一个类的所有类方法。每个类都会有一个单独的meta-class,因为每个类的类方法基本不可能完全相同。
meta-class本身也是一个类,也包含一个isa指针,想想看,这个isa指针指向哪里呢?为了不让这种现象无限延展下去,所有meta-class的isa指向基类的meta-class,以此作为它们的所属类。

二、类与对象的操作函数

runtime提供了大量的函数来操作类与对象。类的操作方法大部分是以class为前缀的,而对象的操作方法大部分是以objc或object_为前缀。

2.1 类操作函数

类操作函数都是针对objc_class结构体中的字段的。

2.1.1成员变量及属性

成员变量操作:

// 获取类中指定名称实例成员变量的信息
Ivar class_getInstanceVariable ( Class cls, const char *name );
它返回一个指向包含name指定的成员变量信息的objc_ivar结构体的指针(Ivar)。

// 获取类成员变量的信息
Ivar class_getClassVariable ( Class cls, const char *name );

// 添加成员变量
BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types );
object-c不支持向已经存在的类中添加成员变量;该方法是向运行时创建的类中添加成员变量,需要注意的是,这个方法只能在objc_allocateClassPair函数与objc_registerClassPair之间调用。

// 获取整个成员变量列表
Ivar * class_copyIvarList ( Class cls, unsigned int *outCount );
outCount指针返回数组的大小。需要注意的是,我们必须使用free()来释放这个数组。

属性操作函数:

// 获取指定的属性
objc_property_t class_getProperty ( Class cls, const char *name );
// 获取属性列表
objc_property_t * class_copyPropertyList ( Class cls, unsigned int *outCount );
// 为类添加属性
BOOL class_addProperty ( Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount );
// 替换类的属性
void class_replaceProperty ( Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount );

2.1.2 方法列表(methodLists)

方法操作函数主要如下:

// 添加方法
BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types );
// 替代方法的实现
IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );

一个object-C方法是一个简单的C函数,它至少包含两个参数—self和_cmd。所以,我们的实现函数(IMP参数指向的函数)至少需要两个参数。
这两个函数的区别:如果类中已经存在name指定的方法,add函数返回NO,replace则会替换方法的实现。如果类中不存在name指定的方法,他们都会创建,并且覆盖父类同名方法的实现。

// 获取实例方法
Method class_getInstanceMethod ( Class cls, SEL name );
// 获取类方法
Method class_getClassMethod ( Class cls, SEL name );
// 获取所有方法的数组
Method * class_copyMethodList ( Class cls, unsigned int *outCount );
前面两个和后面的不同在于,前面两个会搜索父类。

// 返回方法的具体实现
IMP class_getMethodImplementation ( Class cls, SEL name );
IMP class_getMethodImplementation_stret ( Class cls, SEL name );

// 类实例是否响应指定的selector
BOOL class_respondsToSelector ( Class cls, SEL sel );

三、实例(instance)

四、动态创建类和实例

4.1 动态创建类

动态创建类主要有以下方法:
// 创建一个新类和元类
Class objc_allocateClassPair ( Class superclass, const char *name, size_t extraBytes );
extraBytes通常指定为0,该参数是分配给类和元类对象尾部的索引ivars的字节数。

// 销毁一个类及其相关联的类
void objc_disposeClassPair ( Class cls );
如果程序运行中还存在类或其子类的实例,则不能调用针对类调用该方法。

// 在应用中注册由objc_allocateClassPair创建的类
void objc_registerClassPair ( Class cls );
使用objc_allocateClassPair创建的新类,需要使用objc_registerClassPair注册之后才能在程序中使用。

Class cls = objc_allocateClassPair(MyClass.class, “MySubClass”, 0);
class_addMethod(cls, @selector(submethod1), (IMP)imp_submethod1, “v@:”);
class_replaceMethod(cls, @selector(method1), (IMP)imp_submethod1, “v@:”);
class_addIvar(cls, “_ivar1”, sizeof(NSString ), log(sizeof(NSString )), “i”);

objc_property_attribute_t type = {“T”, “@\”NSString\”“};
objc_property_attribute_t ownership = { “C”, “” };
objc_property_attribute_t backingivar = { “V”, “_ivar1”};
objc_property_attribute_t attrs[] = {type, ownership, backingivar};

class_addProperty(cls, “property2”, attrs, 3);
objc_registerClassPair(cls);
之后新创建的类cls就能在程序中使用了。

4.2 动态创建对象

// 创建类实例
id class_createInstance ( Class cls, size_t extraBytes );

// 在指定位置创建类实例
id objc_constructInstance ( Class cls, void *bytes );

// 销毁类实例
void * objc_destructInstance ( id obj );

五、实例操作函数

实例操作函数主要是针对我们创建的实例对象的一系列操作函数;分为三类:针对整个对象进行操作的函数、针对对象实例变量进行操作的函数、针对对象的类进行操作的函数。

5.1 针对整个对象进行操作的函数

// 返回指定对象的一份拷贝
id object_copy ( id obj, size_t size );
// 释放指定对象占用的内存
id object_dispose ( id obj );

假设我们有类A和类B,且类B是类A的子类。类B通过添加一些额外的属性来扩展类A。现在我们创建了一个A类的实例对象,并希望在运行时将这个对象转换为B类的实例对象,这样可以添加数据到B类的属性中。这种情况下,我们没有办法直接转换,因为B类的实例会比A类的实例更大,没有足够的空间来放置对象。此时,我们就要以使用以上几个函数来处理这种情况,如下代码所示:

NSObject *a = [[NSObject alloc] init];
id newB = object_copy(a, class_getInstanceSize(MyClass.class));
object_setClass(newB, MyClass.class);
object_dispose(a);

5.2 针对对象实例变量进行操作的函数

// 修改类实例的实例变量的值
Ivar object_setInstanceVariable ( id obj, const char *name, void *value );
// 获取对象实例变量的值
Ivar object_getInstanceVariable ( id obj, const char *name, void **outValue );
// 返回指向给定对象分配的任何额外字节的指针
void * object_getIndexedIvars ( id obj );
// 返回对象中实例变量的值
id object_getIvar ( id obj, Ivar ivar );
// 设置对象中实例变量的值
void object_setIvar ( id obj, Ivar ivar, id value );

如果实例变量的Ivar已经知道,那么调用object_getIvar会比object_getInstanceVariable函数快,相同情况下,object_setIvar也比object_setInstanceVariable快。

5.3 针对对象的类进行操作的函数

// 返回给定对象的类名
const char * object_getClassName ( id obj );
// 返回对象的类
Class object_getClass ( id obj );
// 设置对象的类
Class object_setClass ( id obj, Class cls );

0 0
原创粉丝点击