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 #import2 #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 #import2 #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 }