iOS runtime 函数的动态加载,以类目的加载为例。
ObjC中函数的动态加载很多地方都有涉及,其中最常见的就是类目(category)了,bundle loading里面有这样一句话:
The executable code files in loadable bundles hold class (and category) definitions that the NSBundle object
can dynamically load while the application runs.
意思是:程序运行是能够动态的加载 bundles 持有类或者类目的定义。
研究动态加载的原理首先要 了解两个结构体:
1 类中每一个方法在内部转换后的结构体
struct objc_method
{
SEL method_name; 函数名称
char *method_types; 函数类型
IMP method_imp; 函数的具体实现 这里是一个指针,指向具体的实现
};
2 每一个类拥有的的函数列表
struct objc_method_list
{
struct objc_method_list *obsolete; 函数列表
int method_count; 类中函数的个数
struct objc_method method_list[1]; 函数列表中的第一个函数地址
};
下面是函数具体的加载过程
0. 定义一个函数
1. 创建一个上面所说的objc_method结构体的实例,显然这个结构体的实例是一个函数,内部的参数由步骤0决定
1.0 为结构体的函数名method_name赋值;
1.1 为结构体的函数实现赋值。(函数指针指向(内存代码区)具体的实现)。
2. 添加objc_method结构体的实例到上面多说的第二个结构体 objc_method_list中
3. 调用class_addMethods方法,将objc_method_list添加到相应的类里面
下面代码示例:
#import <objc/objc-class.h>
// create a class with no methods
@interface EmptyClass : NSObject { }
@end
@implementation EmptyClass
@end
// define the function to add as a method
id sayHello ( id self, SEL _cmd,... )
{
NSLog (@"Hello");
}
void addMethod ()
{
struct objc_method myMethod; <span style="white-space:pre"> </span>//0 创建method实例
myMethod.method_name = sel_registerName("sayHello"); <span style="white-space:pre"> </span>//1.0 为函数名赋值
myMethod.method_imp = sayHello; <span style="white-space:pre"> </span>//1.1 为函数实现赋值
struct objc_method_list * myMethodList;
myMethodList = malloc (sizeof(struct objc_method_list));
myMethodList->method_count = 1;
myMethodList->method_list[0] = myMethod;<span style="white-space:pre"> </span>//2 将自定义的函数实例添加到 list中
class_addMethods ( [EmptyClass class], myMethodList );//3 将list绑定到类上面
// 调用
EmptyClass * instance = [[EmptyClass alloc] init];
[instance sayHello];
[instance release];
}
这基本就是OC动态加载的过程。