Objective-C Runtime总结

144
作者 IamOnelong
2016.04.05 11:22 字数 1239

在伯乐在线看了7篇关于Objective-C Runtime的博文,确定很详尽也很深入。但是看起来有点凌乱,不容易懂。我认为Objective-C Runtime主要包括如下几个方便。

1、数据类型,怎样定义id,对象,class,成员,属性,方法,分类,协议等

2、消息执行逻辑,方法查找,方法缓存,动态添加。

3、对象关联。

在http://www.jianshu.com/p/b1a6c51cfe3e里面的7篇文章说得非常全面了,我就换个角度去总结吧。OC class的定义如下图

1、数据类型:


OC 1.0的就不说了,那都是历史了。上面可以看到class定义里面还是有isa的Class。这个到底是什么呢?带着问题,再看看object的定义。

struct objc_object { Class isa; };

object里面也包含了Class,这个Class又是什么呢?一个对象包含一个Class,我觉得很正常,也是可以直接理解为就是创造这个类对象的类呗,但是为什么class里面还包含了一个Class呢?这个到底是什么呢?这个类难道又是另外一个类创建的呢?细心的同学,的确会这些疑问。事实上OC里面还有一个隐藏的概念(Meta-class),class里面对应的isa就是该类的Meta-class。我觉得这个是OC很神奇的地方,不过也合理。对象方法和类方法分来加载和存放,的确可以节约内存和提高加载的效率。

怎么证明我说的是对的,不是瞎扯的呢?没错就是就是想办法打印类的地址。

NSLog(@"NSObject's class is %p", [NSObject class]);

NSLog(@"NSObject's class is %p",objc_getClass(class_getName([NSObject class])));

NSLog(@"NSObject's meta class is %p",objc_getMetaClass(class_getName([NSObject class])));

想学习的人,还是自己跑一跑代码吧,加深印象,通过打印的确发现两个地址是不同的。

2016-04-05 10:24:06.538 test[806:67538] NSObject's class is 0x765000

2016-04-05 10:24:06.538 test[806:67538] NSObject's class is 0x765000

2016-04-05 10:24:06.538 test[806:67538] NSObject's meta class is 0x765014

这样就证明的确有两个类。那么怎样知道Meta-class里面是存放类方法呢?没错,就是打印方法表。这个留给爱好者去干吧。最后再上一张经典的图,看不懂的就留言吧。


说完了最基本的类,可以说说其他的了,

id的定义

typedef struct objc_object *id;

成员列表


方法列表


方法的定义

typedef struct objc_method *Method;

struct objc_method {

SEL method_name                                          OBJC2_UNAVAILABLE;

char *method_types                                      OBJC2_UNAVAILABLE;

IMP method_imp                                          OBJC2_UNAVAILABLE;

}

成员的定义

typedef struct objc_ivar *Ivar;

struct objc_ivar {

char *ivar_name                                          OBJC2_UNAVAILABLE;

char *ivar_type                                          OBJC2_UNAVAILABLE;

int ivar_offset                                          OBJC2_UNAVAILABLE;

#ifdef __LP64__

int space                                                OBJC2_UNAVAILABLE;

#endif

}

IMP的定义

typedef id (*IMP)(id, SEL, ...);

cache的定义

typedef struct objc_cache *Cache

struct objc_cache {

unsigned int mask

/* total = mask + 1 */

OBJC2_UNAVAILABLE;

unsigned int occupied                                    OBJC2_UNAVAILABLE;

Method buckets[1]                                        OBJC2_UNAVAILABLE;

};

Category的定义

typedef struct objc_category *Category;

struct objc_category {

char *category_name                          OBJC2_UNAVAILABLE; // 分类名

char *class_name                             OBJC2_UNAVAILABLE; // 分类所属的类名

struct objc_method_list *instance_methods    OBJC2_UNAVAILABLE; // 实例方法列表

struct objc_method_list *class_methods       OBJC2_UNAVAILABLE; // 类方法列表

struct objc_protocol_list *protocols         OBJC2_UNAVAILABLE; // 分类所实现的协议列表

}

Protocol的定义

typedef struct objc_object Protocol;

最后我们发现,协议就是一个对象,Category和class很像。我们可以动态创建协议,但是好像没有方法修改Category。

还值得注意的是如下的定义,很有用。

struct objc_super { id receiver; Class superClass; };

2、消息执行逻辑

说到消息,肯定要提到msg_send的,msg_send的原型如下:

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

receiver是一个非常值得关心的东西,例如[self class]和[super class],那他们的receiver到底是谁呢,上面还特意提到了struct objc_super,事实上super的receiver依然是self。

objc_msgSend的执行过程如下,很简单明了。但是这只是正常情况的调用过程。


好吧,我决定再上一张网上找的图,这个图比较全面。


这张图就是比完整了,包括动态调用的流程。这个调用过程没有例子是很难说得清楚的,在这里就不再展开说了,有了这个图,看伯乐在线的博文,也比较容易懂了。有机会的话,我写几个例子玩一玩吧。

3、对象关联

类本身是不支持动态添加成员的,除非你动态创建类的时候添加进去,一旦类注册了就不能再添加了,协议也是如此,一旦发布了,就不能修改了。这样不科学呀,很多时候我们需要动态添加成员,那怎么办呀?于是后来有了对象关联,当然还是可以添加属性的,setValue等

void objc_setAssociatedObject ( id object,constvoid*key, id value, objc_AssociationPolicy policy );

id objc_getAssociatedObject ( id object,constvoid*key );

void objc_removeAssociatedObjects ( id object );

对象关联比较简单,也挺常用的。

好了,关于Objective-C Runtime的总结就到此为止吧。以后有时间再折腾一下下。。。

iOS