OC runtime学习笔记三

来源:互联网 发布:python yield 详解 编辑:程序博客网 时间:2024/05/16 20:27
github Demo: https://github.com/lihei12345/OCRuntimeTest
参考资料:http://blog.csdn.net/colorapp/article/details/43735817


一. Objective-C Associated Objects

Objective-C 不能动态的添加一些属性到对象上,和其他的一些原生支持这点的语言不一样。所以之前你都不得不努力为未来要增加的变量预留好空间。在 Mac OS X 10.6 中,Objective-C 的 Runtime 已经原生的支持这个功能了。涉及的方法主要有三个:
void objc_setAssociatedObject ( id object, const void *key, id value, objc_AssociationPolicy policy );
id objc_getAssociatedObject ( id object, const void *key );
void objc_removeAssociatedObjects ( id object );

示例代码:

@interface NSView (CustomAdditions)
@property(retain) NSImage *customImage;
@end

@implementation NSView (CustomAdditions)
static char img_key; //has a unique address (identifier)
- (NSImage *)customImage {
     return objc_getAssociatedObject(self, &img_key);
}
- (void)setCustomImage:(NSImage *)image {
     objc_setAssociatedObject(self, &img_key, image, OBJC_ASSOCIATION_RETAIN);
}
@end

二. NSObject的+load和+initialize函数
参考:http://www.cnblogs.com/biosli/p/NSObject_inherit.html

1. +load函数
当类被引用进程序的时候会执行这个函数。在一个程序开始运行之前(在main函数开始执行之前),在库开始被程序加载,load函数就会开始被执行。

我们开发的程序都可以认为是一个库,但是库又不会独立存在(我们的程序还会引用其他库,也可能被其他函数引用),所以库的初始化顺序可以如下:
1. 初始化我们引用的库
2. 执行我们自己库的Objective-C的load函数
3. 执行C++和C的static初始化变量
4. 初始化引用我们库的其他库

关于load的执行
1. 在我们的编写的库中,会有很多类重写load函数,他们之间的执行顺序是不确定的。
2. 当父类和子类都实现load函数时,父类的load函数会被先执行。
3. load函数是系统自动加载的,因此不需要调用父类的load函数,否则父类的load函数会多次执行。
4. 在category中写load函数是不会替换原始类中的load函数的,原始类和Category中的load函数都会被执行,原始类的load会先被执行,再执行category中的load函数。当有多个category都实现了load函数,这几个load函数执行顺序不确定。

2. +initialize函数
当类第一次被执行到的时候这个函数会被执行。如果类包含继承关系,父类的initialize函数会比子类先执行。由于是系统自动调用,也不需要显式的调用父类的initialize,否则父类的initialize会被多次执行。假如这个类放到代码中,而这段代码并没有被执行,这个函数是不会被执行的

3. 将针对于类修改放在intialize中,将针对Category的修改放在load中。但是假如我们是修改系统的类,一般会通过添加Category来添加功能,但是如果修改initialize会导致原生的intialize不会执行,所以放在load中会比较妥当。

三. Category的实现
参考:http://chun.tips/blog/2014/11/06/bao-gen-wen-di-objective[nil]c-runtime(3)[nil]-xiao-xi-he-category/

Category的结构定义:
struct category_t {
     const char *name;
     classref_t cls;
     struct method_list_t *instanceMethods;
     struct method_list_t *classMethods;
     struct protocol_list_t *protocols;
     struct property_list_t *instanceProperties;
};


Category里面的方法的加载过程,找到对应的源代码与方法:
1.打开objc源代码,找到 objc-os.mm, 函数_objc_init为runtime的加载入口,由libSystem调用,进行初始化操作。
2.之后调用objc-runtime-new.mm -> map_images加载map到内存
3.之后调用objc-runtime-new.mm->_read_images初始化内存中的map, 这个时候将会load所有的类,协议还有Category。NSOBject的+load方法就是这个时候调用的


在OC runtime初始化的时候,会load所有的类,协议以及category。在load 所有的 category之后,就开始对category进行处理,实例方法被加入到了当前的类对象中, 类方法被加入到了当前类的Meta Class中 (cls->ISA);
需要注意的点是:
1. 如果使用clang rewrite源文件之后,查看被编译器转换的代码,Category头文件是会被注释掉了,结合category的加载过程,可以了解到,即使没有import category的头文件,也能够成功调用到Category方法
2. runtime加载完成后,Category的原始信息在类结构中将不会存在
0 0
原创粉丝点击