OC笔记 - isa | superClass


isa

1 - isa图示

2 - 工作原理

① instance的 isa指向 class:当调用对象方法时,通过 instance的 isa找到 class,最后在 class中找到对象方法进行调用

② class的 isa指向 meta-class:当调用类方法时,通过 class的 isa找到 meta-class,最后在 meta-class中找到类方法进行调用

3 - 从 64位开始,isa需要同 ISA_MASK进行一次位运算才能计算出真实地址

 1 #import 
 2 #import 
 3 // 模拟类对象结构体
 4 struct my_objc_class{
 5     Class isa;
 6 };
 7 
 8 int main(int argc, const char * argv[]) {
 9     
10     // 使用 lldb命令打印对象地址
11     
12     // 实例对象
13     NSObject *object = [[NSObject alloc] init];
14     // po/x (long)object->isa
15     // 0x001dffff91cb8119
16     
17     // 类对象
18     Class objecClass = [NSObject class];
19     // p/x objecClass
20     // 0x00007fff91cb8118 NSObject
21     
22     // 到这里我们发现 object->isa 和 objecClass地址并不一样:0x001dffff91cb8119 != 0x00007fff91cb8118
23     // 需要 isa & ISA_MASK 才能计算出 class的地址
24     // 0x001dffff91cb8119 & ISA_MASK = 0x00007fff91cb8118
25     
26     
27     // 紧接着我们取出 objecClass中的 isa:p/x objecClass->isa
28     // 命令很正确,但是不会显示具体地址,因为类对象、元类对象不再暴露
29     // 不过我们知道,类对象底层实现也是结构体,同样也有 Class型的 isa
30     // 我们在代码最上方模拟出一个类对象结构体
31     
32     struct my_objc_class *objecClass2 = (struct my_objc_class *)objecClass;
33     // p/x objecClass2->isa
34     // 0x00007fff91cb80f0
35     
36     // 0x00007fff91cb80f0 & ISA_MASK = 0x00007fff91cb80f0
37     // 不出意外的话,计算出的结果就是元类对象的地址
38     
39     // 元类对象
40     Class metaClass = object_getClass(objecClass);
41     // p/x metaClass
42     // 0x00007fff91cb80f0
43 
44     // 判断是不是元类
45     NSLog(@"%d",class_isMetaClass(metaClass)); // 1
46     NSLog(@"%@  %@  %@",object,objecClass,metaClass);
47     
48     return 0;
49 }

注:ISA_MASK的取值

superClass

1 - super图示

当 Student的实例对象要调用 Person的对象方法时,会先通过 isa找到 Student的 class,然后通过 superclass找到 Person 的 class,最后找到对象方法的实现进行调用

当 Student的 class要调用 Person的类方法时,会先通过 isa找到 Student的 meta-class,然后通过 superclass找到 Person的 meta-class,最后找到类方法的实现进行调用

结语

1 - isa和 superClass工作示意图

① instance的 isa指向 class

    class的 isa指向 meta-class

    meta-class的 isa指向基类(自身)的 meta-class

② class的 superClass指向父类的 class(如果没有父类,那么 superClass指针是 nil)

    meta-class的 superClass指向父类的 meta-class

    基类的 meta-class的 superClass指向基类的 class

2 - 调用轨迹

① instance调用对象方法的轨迹:isa 找到 class,方法不存在就通过 superClass找父类

② class调用类方法的轨迹:isa 找到 meta-class,方法不存在就通过 superClass找父类

// - NSObject+MakeTest.m

 1 #import "NSObject+MakeTest.h"
 2 @implementation NSObject (MakeTest)
 3 
 4 +(void)doSomethings{
 5     NSLog(@"NSObject doSomethings");
 6 }
 7 
 8 +(void)dealSomethings{
 9        NSLog(@"NSObject dealSomethings");
10 }
11 
12 -(void)eatSomethings{
13      NSLog(@"NSObject eatSomethings");
14 }
15 
16 @end

// - main.m:在 main文件中定义一个 Person

 1 #import 
 2 #import "NSObject+MakeTest.h"
 3 @interface Person : NSObject
 4 
 5 +(void)doSomethings;
 6 -(void)eatSomethings;
 7 
 8 @end
 9 
10 @implementation Person
11 
12 +(void)doSomethings{
13     NSLog(@"Person doSomethings");
14 }
15 
16 -(void)eatSomethings{
17      NSLog(@"Person eatSomethings");
18 }
19 
20 @end
21 
22 int main(int argc, const char * argv[]) {
23     
24     // 完全符合常理:各自调用各自的方法
25     [Person doSomethings];   // Person doSomethings
26     [NSObject doSomethings]; // NSObject doSomethings
27     
28     
29     // 测试一:子无父有,猜一猜调用哪个方法
30     // 调用成功:NSObject的 dealSomethings方法
31     [Person dealSomethings]; // dealSomethings
32     
33     
34     // 测试二:类对象调用对象方法,一样可以调用
35     // 这就验证了基类的 superClass指向自身的 class
36     [Person eatSomethings];   // NSObject eatSomethings
37     [NSObject eatSomethings]; // NSObject eatSomethings
38     
39     // 方法的调用本质上是 objc_msgSend([NSObject class],@selector(eatSomethings))
40     // 它是根据方法名去寻找方法并调用,并不是区分 -号/+号方法
41     return 0;
42 }