runtime的那点事儿(二)消息机制

在微博上分享了他们技术讨论会关于objc runtime的讨论习题内容,习题来自 sunnyxx(博客)。以下是习题内容(图片转自@唐巧_boy微博):


自己做完这些题之后,也顺便复习了一些Objective-C Runtime的知识,现在整理一下,分享给大家。

该笔记分为四篇:

刨根问底Objective-C Runtime(1)- Self & Super

刨根问底Objective-C Runtime(2)- Object & Class & Meta Class

刨根问底Objective-C Runtime(3)- 消息和Category

刨根问底Objective-C Runtime(4)- 成员变量与属性

刨根问底Objective-C Runtime(1)- Self & Super

下面的代码输出什么?

1
2
3
4
5
6
7
8
9
10
11
12
@implementation Son : Father
- (id)init
{
     self = [ super  init];
     if  (self)
     {
         NSLog(@ "%@" , NSStringFromClass([self class]));
         NSLog(@ "%@" , NSStringFromClass([ super  class]));
     }
     return  self;
}
@end

答案:都输出 Son

扫描二维码关注公众号,回复: 3168965 查看本文章
1
2
2014-11-05 11:06:18.060 Test[8566:568584] NSStringFromClass([self class]) = Son
2014-11-05 11:06:18.061 Test[8566:568584] NSStringFromClass([ super  class]) = Son

解惑:这个题目主要是考察关于objc中对 self 和 super 的理解。

self 是类的隐藏参数,指向当前调用方法的这个类的实例。而 super 是一个 Magic Keyword, 它本质是一个编译器标示符,和 self 是指向的同一个消息接受者。上面的例子不管调用[self class]还是[super class],接受消息的对象都是当前 Son *xxx 这个对象。而不同的是,super是告诉编译器,调用 class 这个方法时,要去父类的方法,而不是本类里的。

当使用 self 调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;而当使用 super 时,则从父类的方法列表中开始找。然后调用父类的这个方法。

真的是这样吗?继续看:

使用clang重写命令:

1
$ clang -rewrite-objc test.m

发现上述代码被转化为:

1
2
NSLog((NSString *)&__NSConstantStringImpl__var_folders_gm_0jk35cwn1d3326x0061qym280000gn_T_main_a5cecc_mi_0, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName( "class" ))));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_gm_0jk35cwn1d3326x0061qym280000gn_T_main_a5cecc_mi_1, NSStringFromClass(((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){ (id)self, (id)class_getSuperclass(objc_getClass( "Son" )) }, sel_registerName( "class" ))));

从上面的代码中,我们可以发现在调用 [self class] 时,会转化成 objc_msgSend函数。看下函数定义:

1
id objc_msgSend(id self, SEL op, ...)

我们把 self 做为第一个参数传递进去。

而在调用 [super class]时,会转化成 objc_msgSendSuper函数。看下函数定义:

1
id objc_msgSendSuper(struct objc_super * super , SEL op, ...)

第一个参数是 objc_super 这样一个结构体,其定义如下:

1
2
3
4
struct objc_super {
    __unsafe_unretained id receiver;
    __unsafe_unretained Class super_class;
};

结构体有两个成员,第一个成员是 receiver, 类似于上面的 objc_msgSend函数第一个参数self 。第二个成员是记录当前类的父类是什么。

所以,当调用 [self class] 时,实际先调用的是 objc_msgSend函数,第一个参数是 Son当前的这个实例,然后在 Son 这个类里面去找 - (Class)class这个方法,没有,去父类 Father里找,也没有,最后在 NSObject类中发现这个方法。而 - (Class)class的实现就是返回self的类别,故上述输出结果为 Son。

objc Runtime开源代码对- (Class)class方法的实现:

1
2
3
- (Class)class {
     return  object_getClass(self);
}

而当调用 [super class]时,会转换成objc_msgSendSuper函数。第一步先构造 objc_super 结构体,结构体第一个成员就是 self 。第二个成员是 (id)class_getSuperclass(objc_getClass(“Son”)) , 实际该函数输出结果为 Father。第二步是去 Father这个类里去找- (Class)class,没有,然后去NSObject类去找,找到了。最后内部是使用 objc_msgSend(objc_super->receiver, @selector(class))去调用,此时已经和[self class]调用相同了,故上述输出结果仍然返回 Son。

刨根问底Objective-C Runtime(2)- Object & Class & Meta Clas

本篇笔记主要是讲述objc runtime中关于Object & Class & Meta Class的细节。

习题内容

下面代码的运行结果是?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@interface Sark : NSObject
@end
@implementation Sark
@end
int main(int argc, const char * argv[]) {
     @autoreleasepool {
         BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
         BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
         BOOL res3 = [(id)[Sark class] isKindOfClass:[Sark class]];
         BOOL res4 = [(id)[Sark class] isMemberOfClass:[Sark class]];
         NSLog(@ "%d %d %d %d" , res1, res2, res3, res4);
     }
     return  0;
}

运行结果为:

1
2014-11-05 14:45:08.474 Test[9412:721945] 1 0 0 0

这里先看几个概念

什么是 id

id 在 objc.h 中定义如下:

1
2
/// A pointer to an instance of a class.
typedef struct objc_object *id;

就像注释中所说的这样 id 是指向一个 objc_object 结构体的指针。

id 这个struct的定义本身就带了一个 *, 所以我们在使用其他NSObject类型的实例时需要在前面加上 *, 而使用 id 时却不用。

那么objc_object又是什么呢

objc_object 在 objc.h 中定义如下:

1
2
3
4
/// Represents an instance of a class.
struct objc_object {
     Class isa;
};

这个时候我们知道Objective-C中的object在最后会被转换成C的结构体,而在这个struct中有一个 isa 指针,指向它的类别 Class。

那么什么是Class呢

在 objc.h 中定义如下:

1
2
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

我们可以看到 Class本身指向的也是一个C的struct objc_class。

继续看在runtime.h中objc_class定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct objc_class {
     Class isa  OBJC_ISA_AVAILABILITY;
     #if !__OBJC2__
     Class super_class                                        OBJC2_UNAVAILABLE;
     const char *name                                         OBJC2_UNAVAILABLE;
     long version                                             OBJC2_UNAVAILABLE;
     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;

该结构体中,isa 指向所属Class, super_class指向父类别。

继续看

下载objc源代码,在 objc-runtime-new.h 中,我们发现 objc_class有如下定义:

1
2
3
4
5
6
struct objc_class : objc_object {
     // Class ISA;
     Class superclass;   
     ...
     ...
}

豁然开朗,我们看到在Objective-C的设计哲学中,一切都是对象。Class在设计中本身也是一个对象。而这个Class对象的对应的类,我们叫它 Meta Class。即Class结构体中的 isa 指向的就是它的 Meta Class。

Meta Class

根据上面的描述,我们可以把Meta Class理解为 一个Class对象的Class。简单的说:

1
2
当我们发送一个消息给一个NSObject对象时,这条消息会在对象的类的方法列表里查找
当我们发送一个消息给一个类时,这条消息会在类的Meta Class的方法列表里查找

而 Meta Class本身也是一个Class,它跟其他Class一样也有自己的 isa 和 super_class 指针。看下图:

02.jpg

  • 每个Class都有一个isa指针指向一个唯一的Meta Class

  • 每一个Meta Class的isa指针都指向最上层的Meta Class(图中的NSObject的Meta Class)

  • 最上层的Meta Class的isa指针指向自己,形成一个回路

  • 每一个Meta Class的super class指针指向它原本Class的 Super Class的Meta Class。但是最上层的Meta Class的 Super Class指向NSObject Class本身

  • 最上层的NSObject Class的super class指向 nil

解惑

为了更加清楚的知道整个函数调用过程,我们使用clang -rewrite-objc main.m重写,可获得如下代码:

1
2
3
4
BOOL res1 = ((BOOL (*)(id, SEL, Class))(void *)objc_msgSend)((id)((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass( "NSObject" ), sel_registerName( "class" )), sel_registerName( "isKindOfClass:" ), ((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass( "NSObject" ), sel_registerName( "class" )));
BOOL res2 = ((BOOL (*)(id, SEL, Class))(void *)objc_msgSend)((id)((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass( "NSObject" ), sel_registerName( "class" )), sel_registerName( "isMemberOfClass:" ), ((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass( "NSObject" ), sel_registerName( "class" )));
BOOL res3 = ((BOOL (*)(id, SEL, Class))(void *)objc_msgSend)((id)((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass( "Sark" ), sel_registerName( "class" )), sel_registerName( "isMemberOfClass:" ), ((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass( "NSObject" ), sel_registerName( "class" )));
BOOL res4 = ((BOOL (*)(id, SEL, Class))(void *)objc_msgSend)((id)((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass( "Sark" ), sel_registerName( "class" )), sel_registerName( "isMemberOfClass:" ), ((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass( "NSObject" ), sel_registerName( "class" )));

先看前两个调用:

  • 最外层是 objc_msgSend函数,转发消息。

  • 函数第一个参数是 (id)((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class"))

  • 函数第二个参数是转发的selector

  • 函数第三个参数是 ((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class"))

我们注意到第一个参数和第三个参数对应重写的是[NSObject class],即使用objc_msgSend向 NSObject Class 发送 @selector(class) 这个消息

打开objc源代码,在 Object.mm 中发现+ (Class)class实现如下:

1
2
3
+ (Class)class {
     return  self;
}

所以即返回Class类的对象本身。看如下输出:

1
2
3
4
NSLog(@ "%p" , [NSObject class]);
NSLog(@ "%p" , [NSObject class]);
2014-11-05 18:48:30.939 Test[11682:865988] 0x7fff768d40f0
2014-11-05 18:48:30.940 Test[11682:865988] 0x7fff768d40f0

继续打开objc源代码,在 Object.mm 中,我们发现 isKindOfClass的实现如下:

1
2
3
4
5
6
7
8
- (BOOL)isKindOf:aClass
{
     Class cls;
     for  (cls = isa; cls; cls = cls->superclass) 
         if  (cls == (Class)aClass)
             return  YES;
     return  NO;
}

对着上面Meta Class的图和实现,我们可以看出

  • 当 NSObject Class对象第一次进行比较时,得到它的isa为 NSObject的Meta Class, 这个时候 NSObject Meta Class 和 NSObject Class不相等。

  • 然后取NSObject 的Meta Class 的Super class,这个时候又变成了 NSObject Class, 所以返回相等。

所以上述第一个输出结果是 YES 。

我们在看下 ‘isMemberOfClass’的实现:

1
2
3
4
- (BOOL)isMemberOf:aClass
{
     return  isa == (Class)aClass;
}

综上所述,当前的 isa 指向 NSObject 的 Meta Class, 所以和 NSObject Class不相等。

所以上述第二个输出结果为 NO 。

继续看后面两个调用:

  • Sark Class 的isa指向的是 Sark的Meta Class,和Sark Class不相等

  • Sark Meta Class的super class 指向的是 NSObject Meta Class, 和 Sark Class不相等

  • NSObject Meta Class的 super class 指向 NSObject Class,和 Sark Class 不相等

  • NSObject Class 的super class 指向 nil, 和 Sark Class不相等

所以后面两个调用的结果都输出为 NO 。


猜你喜欢

转载自blog.csdn.net/jq2530469200/article/details/51886532