三. superclass
superclass指针是相对于class对象与meta-class对象来说的
定义两个Class:Person继承于NSObject,Student继承于Person。现在有个场景:通过Student的instance对象调用Person中实现的实例方法,具体的调用过程如下:
通过Student类的instance对象的isa找到对应的Student类的class对象,但没有找到相关的实现,系统会继续到superclass中找,于是会到Person类的class对象中找到具体的实现并调用
类方法的调用也是一样的
四. isa与superclass
在NSObject的头文件中,可以看到一个Class类型的isa成员变量
@interface NSObject <NSObject> { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wobjc-interface-ivars" Class isa OBJC_ISA_AVAILABILITY; #pragma clang diagnostic pop }
在NSObject的协议里有个Class类型的成员变量superclass
@protocol NSObject ··· @property (readonly) Class superclass; ··· @end
接下来,我们看一个经典的指针逻辑分析图,分别看Objective-C面向对象语言设计、函数调用、成员变量的存储和访问是如何实现的,为什么不能多继承
image
这张图可以看到三个类:Rootclass
、Superclass
、SubClass
,分别为基类
、父类
和子类
,图中左到右分别为实例对象
、类对象
、元类
, 举个例子:我们创建一个Person
类继承自NSObject
, 创建一个Student
类继承自Person
。则NSObject
、Peron
、Student
分别对应Rootclass
、Superclass
、SubClass
,左边的Instance对象代表实例对象,也就是我们alloc
出来的实例
由图可知:
isa
- 实例的isa指向类对象,类对象的isa指向元类对象
- meta-class的isa都指向基类的meta-class
superclass
- class的superclass指向父类,如果没有父类,指向nil
- meta的superclass指向父类的meta-class,基类的meta-class的superclass指向基类的class
调用轨迹
- instance对象:isa找到class,方法如果不存在,就通过superclass找父类
- class对象:isa找到meta-class,方法如果不存在,就通过superclass找父类
五. isa、class、superclass关系求证
上面说到一句话
instance对象的isa值是class对象, class的isa值是meta-class对象
上图也有所体现了,再把上图标识序号,如下:
image
接下来就是证明这5条线的正确性,创建Person类继承自NSObject,代码如下:
Person *instanceObj = [[Person alloc] init]; // 第一条线 (instance的isa值是Class对象) Class classCls = object_getClass(instanceObj); // 第二条线 (Class对象的isa值是meta-class) Class metaCls = object_getClass(classCls); // 第三条线 (meta-class的Superclass 是 RootClass的meta-class) Class rootMetaCls0 = class_getSuperclass(metaCls); // 第四条线 (meta-class的isa值是RootClass 的meta-class) Class rootMetaCls1 = object_getClass(metaCls); //RootClass的meta-class Class rootMetaCls = object_getClass(rootMetaCls0); NSLog(@"\n instanceObj = %p \n classCls = %p \n metaCls = %p \n rootMetaCls0 = %p \n rootMetaCls1 = %p \n rootMetaCls = %p\n", instanceObj, classCls, metaCls, rootMetaCls0, rootMetaCls1, rootMetaCls);
打印结果:
2021-05-04 16:25:08.423125+0800 ClassDemo[32740:9240333] instanceObj = 0x600002d7c0e0 classCls = 0x109a21800 metaCls = 0x109a217d8 rootMetaCls0 = 0x7fff86d48638 rootMetaCls1 = 0x7fff86d48638 rootMetaCls = 0x7fff86d48638
注意下后边三个值是一样的,现在应该理清了,但是上面的代码中,没有isa
相关的,我们仅仅是获取了对应对象的类型(Class)而已,现在想要看看对应的isa值是多少,由于isa有保护,我们可以通过kvc方式获取,也可以参考写个Class
结构体.通过Bridge强转桥接C/C++再访问isa
对象地址,前提是你要通过clang编译器编译出对象结构体,知道它长什么样。
我们采用kvc的方式,对代码稍作修改,查看isa值:
NSObject *object = [[NSObject alloc] init]; Class rootCls = object_getClass(object); Person *instanceObj = [[Person alloc] init]; // 第一条线 (instance的isa值是Class对象) Class classCls = object_getClass(instanceObj); // 第二条线 (Class对象的isa值是meta-class) Class metaCls = object_getClass(classCls); // 第三条线 (meta-class的Superclass 是 RootClass的meta-class) Class rootMetaCls0 = class_getSuperclass(metaCls); // 第四条线 (meta-class的isa值是RootClass 的meta-class) Class rootMetaCls1 = object_getClass(metaCls); //RootClass的meta-class Class rootMetaCls = object_getClass(rootMetaCls0); //root-meta-class 的superclass Class rootMetaSuperCls = class_getSuperclass(rootMetaCls0); NSLog(@"\n rootCls = %p \n instanceObj = %p \n classCls = %p \n metaCls = %p \n rootMetaCls0 = %p \n rootMetaCls1 = %p \n rootMetaCls = %p \n rootMetaSuperCls = %p \n", rootCls, instanceObj, classCls, metaCls, rootMetaCls0, rootMetaCls1, rootMetaCls, rootMetaSuperCls); //通过kvc方式获取isa指针 NSLog(@"\n instanceObj-isa = %p \n classCls-isa = %p \n metaCls-isa = %p \n rootMetaCls0-isa = %p \n", [instanceObj valueForKey:@"isa"], [classCls valueForKey:@"isa"], [metaCls valueForKey:@"isa"], [rootMetaCls0 valueForKey:@"isa"]);
打印结果为:
2021-05-04 16:55:11.688427+0800 ClassDemo[49442:9299385] rootCls = 0x7fff86d48660 instanceObj = 0x600003e38170 classCls = 0x10e5b4810 metaCls = 0x10e5b47e8 rootMetaCls0 = 0x7fff86d48638 rootMetaCls1 = 0x7fff86d48638 rootMetaCls = 0x7fff86d48638 rootMetaSuperCls = 0x7fff86d48660 2021-05-04 16:55:11.688742+0800 ClassDemo[49442:9299385] instanceObj-isa = 0x10e5b4810 classCls-isa = 0x10e5b47e8 metaCls-isa = 0x7fff86d48638 rootMetaCls0-isa = 0x7fff86d48638
根据打印结果很明显:
- instance对象的isa指向Class对象(0x10657a808, 第一条线)
- Class对象的isa指向meta-class对象(0x10657a7e0, 第二条线)
- meta-class的Superclass是root-meta-class (0x7fff86d48638, 第三条线)
- meta-class的isa指向 root-meta-class (0x7fff86d48638, 第四条线)
- root-meta-class的isa指向它自己 (0x7fff86d48638, 第五条线)
- root-meta-class的Superclass是Rootclass (0x7fff86d48660)
六. 拓展
由于基元类的Superclass指向基类,我们会发现一个神奇的问题:如果一个类方法一直找不到,发现基类的对象方法存在,则会调用基类的对象方法,也就是子类的一个+方法,调用了基类的一个-方法。这是因为iOS的消息方法机制调用的时候没有区分对象方法和类方法,代码验证一下:
定义一个NSObject类别,里面给NSObject加一个对象方法
.h @interface NSObject (category) - (void)showName; @end .m @implementation NSObject (category) - (void)showName { NSLog(@"NSObject (category) class = %@", NSStringFromClass([self class])); } @end
Person继承自NSObject,在.h声明+ (void)showName
方法不实现(为了编译器不报错,也可以用runtime发消息调用)
.h @interface Person : NSObject + (void)showName; @end .m @implementation Person @end
调用
[Person showName];
我们在调用Person
的+ (void)showName
方法竟然执行了NSObject category里定义的- (void)showName
方法
2021-05-04 17:09:56.436515+0800 ClassDemo[51930:9318834] NSObject (category) class = Person
这也很好的证明了基元类对象的Superclass指向基类