本文是参考南峰子Objective-C Runtime系列文章
做一个自己的总结,强烈推荐查看原文
1、runtime介绍
Objective-C 是一门动态语言,它将很多静态语言在编译和连接时期做的事情放到了运行时来处理,这种动态语言的优势在于:我们写代码时更具有灵活性,如我们可以把消息转发给我们想要转发的对象,或者随意交换一个方法等。
这种特性意味着Objective-C不仅需要一个编译器,还需要一个运行时动态库来执行编译的代码。对于Objective-C来说,这个运行时系统就像一个操作系统一样:让所有工作正常运行。这个运行时系统就是Objc Runtime ,Objc Runtime其实是一个Runtime库,基本上是用C语言和汇编语言写的,使得C语言有了面向对象的能力。
Runtime库主要做了以下两件事:
- 封装:在这个库中,对象可以使用
C语言中的结构体表示,方法可以使用C函数来实现,另外再加上一些额外的特性,这些结构体和函数被runtime函数封装后,我们就可以在程序运行时创建、检查、修改类、对象、方法了 - 找出方法的最终执行代码:当程序执行
[object doSomething]时,会向接受者(object)发送一条消息(doSomething),runtime会根据消息接受者能否响应该消息而做出不同反应
Objective-C runtime目前有两个版本:Modern runtime和Legacy runtime。Modern Runtime覆盖了64位的Mac OS X Apps,还有iOS Apps,Legacy Runtime是早期用来给32位 Mac OS X Apps 用的,已经过时
苹果和GNU各自维护一个开源的 runtime 版本,这两个版本之间都在努力的保持一致。
高级编程语言想要成为可执行文件,需要先编译成汇编语言,再汇编为机器语言,机器语言是计算机唯一能识别的语言(转换过程待研究)
2、类
Objective-C类是有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; // 类的版本信息,默认为0
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;
1.isa : 需要注意的是在Objective-C中,所有的类自身也是一个对象,这个对象的Class里面也有一个isa指针,它指向metaClass(元类)
super_class:指向该类的父类,如果该类已经是最顶层的根类(如NSObject或NSProxy),则super_class为NULLname: 类名version:我们可以使用这个字段来提供类的版本信息。这对于对象的序列化非常有用,它可是让我们识别出不同类定义版本中实例变量布局的改变。info: 类信息instance_size: 该类的对象大小ivars: 成员变量的列表methodLists: 方法列表cathe: 用于缓存最近使用的方法。
一个接收者对象接收到一个消息时,它会根据isa指针去查找能够响应这个消息的对象。在实际使用中,这个对象只有一部分方法是常用的,很多方法其实很少用或者根本用不上。这种情况下,如果每次消息来时,我们都是methodLists中遍历一遍,性能势必很差。这时,cache就派上用场了。在我们每次调用过一个方法后,这个方法就会被缓存到cache列表中,下次调用的时候runtime就会优先去cache中查找,如果cache没有,才去methodLists中查找方法。这样,对于那些经常用到的方法的调用,提高了调用的效率。protocols: 协议列表
3、对象
objc_object是表示一个类的实例的结构体
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
这个结构体只有一个属性,即指向其类的isa指针。这样,当我们向一个Objective-C对象发送消息时,运行时库会根据实例对象的isa指针找到这个实例对象所属的类。Runtime库会在类的方法列表及父类的方法列表中去寻找与消息对应的selector指向的方法,找到后即运行这个方法。
4、元类(Meta Class)
meta-class是一个类对象的类
当我们向一个对象发送消息时,runtime会在这个对象所属的这个类的方法列表中查找方法;
而向一个类发送消息时,会在这个类的meta-class的方法列表中查找方法
meta-class之所以重要,是因为它存储着一个类的所有类方法。每个类都会有一个单独的meta-class,因为每个类的类方法基本不可能完全相同
meta-class也是一个类,也可以向它发送一个消息,那么它的isa又是指向什么呢?为了不让这种结构无限延伸下去,Objective-C的设计者让所有的meta-class的isa指向基类的meta-class,以此作为它们的所属类。
即,任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己的所属类,而基类的meta-class的isa指针是指向它自己。这样就形成了一个完美的闭环。

对于NSObject继承体系来说,其实例方法对体系中的所有实例、类和meta-class都是有效的;而类方法对于体系内的所有类和meta-class都是有效的。
5、objc_cache
cathe用于缓存调用过的方法。这个字段是一个指向objc_cache结构体的指针,其定义如下:
struct objc_cache {
unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE;
unsigned int occupied OBJC2_UNAVAILABLE;
Method _Nullable buckets[1] OBJC2_UNAVAILABLE;
};
-
mask:一个整数,指定分配的缓存bucket的总数。在方法查找过程中,runtime使用这个字段来确定开始线性查找数组的索引位置。指向方法selector的指针与该字段做一个AND位操作(index = (mask & selector))。这可以作为一个简单的hash散列算法。 -
occupied:一个整数,指定实际占用的缓存bucket的总数。 -
buckets:指向Method数据结构指针的数组。这个数组可能包含不超过mask+1个元素。需要注意的是,指针可能是NULL,表示这个缓存bucket没有被占用,另外被占用的bucket可能是不连续的。这个数组可能会随着时间而增长。
6、类与对象的操作函数
类的操作方法大部分是以class_为前缀的
对象的操作方法大部分是以objc_或object_为前缀
在runtime.h里面搜索Working with ,一共有8个搜索结果,搜索顺序分别是:
Working with InstancesWorking with ClassesWorking with MethodsWorking with Instance VariablesWorking with PropertiesWorking with ProtocolsWorking with LibrariesWorking with Selectors
这里我们主要看Working with Classes、Working with Instances、 Working with Instance Variables里面的方法
6.1、动态创建类
//创建一个新类和元类
Class objc_allocateClassPair(Class _Nullable superclass, const char * _Nonnull name,
size_t extraBytes) ;
// 在应用中注册由objc_allocateClassPair创建的类
void objc_registerClassPair(Class _Nonnull cls) ;
//销毁一个类极其相关类
Class objc_duplicateClass(Class _Nonnull original, const char * _Nonnull name,
size_t extraBytes);
objc_allocateClassPair函数:如果我们要创建一个根类,则superclass指定为Nil。extraBytes通常指定为0,该参数是分配给类和元类对象尾部的索引ivars的字节数。objc_disposeClassPair函数用于销毁一个类,不过需要注意的是,如果程序运行中还存在类或其子类的实例,则不能调用针对类调用该方法。
创建一个新类,先用objc_allocateClassPair,调用class_addMethod,class_addIvar等函数来为新创建的类添加方法、实例变量和属性等。再调用objc_registerClassPair函数来注册类,之后这个新类就可以在程序中使用了。
实例方法和实例变量应该添加到类自身上,而类方法应该添加到类的元类上
6.2、动态创建对象
// 创建类实例
id class_createInstance ( Class cls, size_t extraBytes );
// 在指定位置创建类实例
id objc_constructInstance ( Class cls, void *bytes );
// 销毁类实例
void * objc_destructInstance ( id obj );
class_createInstance:创建实例时,会在默认的内存区域为类分配内存。extraBytes额外的字节可以用来存储类定义中定义之外的其他实例变量.objc_constructInstance:在指定的位置(bytes)创建类实例。objc_destructInstance:销毁一个类的实例,但不会释放并移除任何与其相关的引用。
类和对象的相关操作函数
// 返回指定对象的一份拷贝
id object_copy ( id obj, size_t size );
// 释放指定对象占用的内存
id object_dispose ( id obj );
// 修改类实例的实例变量的值
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 );
// 返回给定对象的类名
const char * object_getClassName ( id obj );
// 返回对象的类
Class object_getClass ( id obj );
// 设置对象的类
Class object_setClass ( id obj, Class cls );
// 获取已注册的类定义的列表
int objc_getClassList ( Class *buffer, int bufferCount );
// 创建并返回一个指向所有已注册类的指针列表
Class * objc_copyClassList ( unsigned int *outCount );
// 返回指定类的类定义
Class objc_lookUpClass ( const char *name );
Class objc_getClass ( const char *name );
Class objc_getRequiredClass ( const char *name );
// 返回指定类的元类
Class objc_getMetaClass ( const char *name );
6.3、父类(super_class)和元类(meta-class)
// 获取类的父类
Class class_getSuperclass (Class _Nullable cls);
// 判断给定的Class是否是一个元类
BOOL class_isMetaClass (Class _Nullable cls);
6.4、类名
// 获取类的类名
const char * class_getName (Class _Nullable cls);
6.5、版本号
// 获取版本号
int class_getVersion (Class _Nullable cls) ;
// 设置版本号
void class_setVersion (Class _Nullable cls, int version);
6.6、实例大小
// 获取实例大小
size_t class_getInstanceSize (Class _Nullable cls);
6.7 成员变量
// 获取类中指定名称实例成员变量的信息
Ivar class_getInstanceVariable (Class _Nullable cls, const char * _Nonnull name);
// 获取类成员变量的信息
Ivar class_getClassVariable(Class _Nullable cls, const char * _Nonnull name);
// 添加成员变量
BOOL class_addIvar (Class _Nullable cls, const char * _Nonnull name, size_t size,
uint8_t alignment, const char * _Nullable types) ;
// 获取整个成员变量列表
Ivar * class_copyIvarList (Class _Nullable cls, unsigned int * _Nullable outCount) ;
-
class_getInstanceVariable: 根据name返回指定的对象成员变量信息(objc_ivar结构体)
class_getClassVariable: 根据name返回指定的类成员变量信息(objc_ivar结构体),一般认为Objective-C不支持类变量class_addIvar: 参数分别是,成员变量所属类、成员变量名、对齐方式、成员变量类型
Objective-C不支持往已存在的类中添加实例成员变量,因此不管是系统库提供的提供的类,还是我们自定义的类,都无法动态添加成员变量,但是动态创建类的可以添加成员变量,只能在objc_allocateClassPair与objc_registerClassPair之间
如添加一个NSString类型的ivar1变量
class_addIvar(cls, "ivar1", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));
-
class_copyIvarList: 它返回一个指向成员变量信息的数组,数组中每个元素是指向该成员变量信息的objc_ivar结构体的指针,outCount指针返回数组的大小
注意 : 返回的列表不包含父类的成员变量和属性,必须使用free()来释放这个数组
6.8 属性
// 获取指定的属性
objc_property_t class_getProperty(Class _Nullable cls, const char * _Nonnull name);
// 获取属性列表
objc_property_t class_copyPropertyList(Class _Nullable cls, unsigned int * _Nullable outCount);
// 为类添加属性
BOOL class_addProperty(Class _Nullable cls, const char * _Nonnull name,
const objc_property_attribute_t * _Nullable attributes,
unsigned int attributeCount);
// 替换类的属性
void class_replaceProperty(Class _Nullable cls, const char * _Nonnull name,
const objc_property_attribute_t * _Nullable attributes,
unsigned int attributeCount)
-
class_getProperty: 根据name返回指定的类属性(objc_property结构体) -
class_copyPropertyList: 它返回一个指向属性的数组,数组中每个元素是指向该属性的objc_property结构体的指针,outCount指针返回数组的大小
注意 : 返回的列表不包含父类的属性,必须使用free()来释放这个数组
-
class_addProperty: 参数含义分别是:类名、属性名、属性特性、属性特性个数
如添加属性名为@property (nonatomic , copy) NSString *property1;的属性
objc_property_attribute_t type = { "T", "@\"NSString\"" };
objc_property_attribute_t ownership = { "C", "" }; // C = copy
objc_property_attribute_t nonatomic = { "N", "" }; //nonatomic
objc_property_attribute_t backingivar = { "V", "_property1" };//V 实例变量
objc_property_attribute_t attrs[] = { type, ownership,nonatomic, backingivar };
class_addProperty(cls, "property1", attrs, 4);
-
class_replaceProperty: 替换类的属性
6.9、方法
//添加方法
BOOL class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types);
//获取实例方法
Method class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name);
//获取类方法
Method class_getClassMethod(Class _Nullable cls, SEL _Nonnull name);
//获取所有方法数组
Method class_copyMethodList(Class _Nullable cls, unsigned int * _Nullable outCount);
//替代方法的实现
IMP class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types) ;
//返回方法的具体实现
IMP class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name) ;
IMP class_getMethodImplementation_stret(Class _Nullable cls, SEL _Nonnull name) ;
//对象是否响应指定的selector
BOOL class_respondsToSelector(Class _Nullable cls, SEL _Nonnull sel) ;
1.class_addMethod : 参数含义分别是:添加方法的类、添加方法的编号、方法实现、方法类型,使用这个方法添加方法会覆盖父类的方法实现,但不会取代本类中已存在的实现,如果本类中包含一个同名的实现,则函数会返回NO。如果要修改已存在实现,可以使用method_setImplementation
如添加一个eat方法
class_addMethod([self class],@selector(eat:),(IMP) eatIMP, "v@:@");
-
class_getInstanceMethod: 根据name返回指定的对象方法(objc_method结构体) -
class_getClassMethod: 根据name返回指定的类方法(objc_method结构体)
注意 : 这两个函数都会去搜索父类的实现
-
class_copyMethodList: 返回一个指向实例方法的数组,数组中每个元素都是该方法信息的objc_method结构体指针,outCount返回数组的大小。
如果需要获取类方法,则可以使用class_copyMethodList(object_getClass(cls), &count),因为一个类的实例方法是定义在元类里面
注意 : 返回的列表不包含父类的方法,必须使用free()来释放这个数组
class_replaceMethod: 如果类中不存在name指定的方法,则类似于class_addMethod函数一样会添加方法;如果类中已存在name指定的方法,则类似于method_setImplementation一样替代原方法的实现。class_getMethodImplementation: 返回一个指向方法实现函数的指针,在向类实例发送消息时会被调用,这个函数会比method_getImplementation(class_getInstanceMethod(cls, name))更快。返回的函数指针可能是一个指向runtime内部的函数,而不一定是方法的实际实现。例如,如果类实例无法响应selector,则返回的函数指针将是运行时消息转发机制的一部分。class_respondsToSelector: 我们通常使用NSObject类的respondsToSelector:或instancesRespondToSelector:方法来达到相同目的。
6.10、协议
// 添加协议
BOOL class_addProtocol ( Class cls, Protocol *protocol );
// 返回类是否实现指定的协议
BOOL class_conformsToProtocol ( Class cls, Protocol *protocol );
// 返回类实现的协议列表
Protocol * class_copyProtocolList ( Class cls, unsigned int *outCount );
class_conformsToProtocol: 可以使用NSObject类的conformsToProtocol:方法来替代。class_copyProtocolList: 返回一个指向协议的数组,数组中每个元素都是该协议信息的objc_object结构体指针,outCount返回数组的大小。
注意 : 返回的列表不包含父类的协议,必须使用free()来释放这个数组