一、super的本质
有一个Person类和一个Student类,Student类继承自Person类。现在在Student类的init方法中有如下代码,问打印结果是什么:
NSLog(@"[self class] = %@", [self class]);
NSLog(@"[super class] = %@", [super class]);
NSLog(@"[self superclass] = %@", [self superclass]);
NSLog(@"[super superclass] = %@", [super superclass]);
按照以前的理解,第一个应该是Student类的类对象,第二个是student类的父类的类对象,也就是Person类的类对象,第三个也是Student类的父类的类对象,第四个是Student类的父类的父类的类对象也就是NSObject类的类对象。
// 打印结果
[self class] = Student
[super class] = Student
[self superclass] = Person
[super superclass] = Person
通过打印结果可以看到,第二个和第四个与我之前理解的不一致,这是为什么呢?[super class]的打印结果为什么是Student呢?在搞清楚这些问题之前,我们先搞清楚class和superclass方法的实现。我在runtime的NSObject.mm文件中找到了实现的源码:
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
+ (Class)superclass {
return self->superclass;
}
- (Class)superclass {
return [self class]->superclass;
}
//通过对象的isa指针获取类的类对象
Class object_getClass(id obj) {
if (obj) return obj->getIsa();
else return Nil;
}
Class class_getSuperclass(Class cls) {
if (!cls) return nil;
return cls->superclass;
}
为了搞清楚super的实现,我在Person类中实现run方法,然后在Student类的init方法中使用[super run]来调用,然后将其转化为C++的源码,找到[super run]的实现:
objc_msgSendSuper(__rw_objc_super{
self,
class_getSuperclass(objc_getClass("Student")},
@selector(run));
objc_msgSendSuper()函数中传入了两个参数,一个是一个结构体,还有一个就是消息@selector(run)。第一个参数这个结构体中有两个成员变量,一个是self也就是Student实例对象,还有一个是Student类的父类的类对象,也即是Person类的类对象。
查看看一下objc_msgSendSuper的实现
objc_msgSendSuper()中相当于传入了三个参数:objc_msgSendSuper(object ,superclass, @selector(run)),第一个参数是消息的接收者,第二个参数决定了从这个父类对象开始寻找方法的实现,第三个参数就是消息。
所以回到上面的打印结果:
[self class]
通过实例对象的isa指针找到找到其类对象,所以打印是Student。
[super class]
给self对象发送@selector(class)消息,但是class方法的实现要从Person类对象开始查找。class方法是在基类NSObject类中实现的,所以不管是从Student类对象中开始查找还是从Person类对象中开始查找方法的实现,做种都是找打NSObject的实现中,所以[self class]和[super class]并无差异,打印都是Student。
[self superclass]
获取的就是自己的类对象的superclass指针的指向,就是父类的类对象,所以打印是Person。
[super superclass]
给student对象发送@selector(superclass)消息,但是superclass的实现要从父类Person类的类对象开始找起,但是superclass的实现是基类NSObject类实现的,所以从Student类的类对象和Person类的类对象开始查是没有区别的。最终输出都是student对象的父类对象,打印结果是Person。
二、isKindOfClass ,isMemberOfClass
BOOL res1 = [[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [[Person class] isKindOfClass:[Person class]];
BOOL res4 = [[Person class] isMemberOfClass:[Person class]];
NSLog(@"%d, %d, %d, %d", res1, res2, res3, res4);
打印结果:
1, 0, 0, 0
查看isMemberOfClass:和isKindOfClass:的源码具体实现:
//object_getClass()取得的是对象的isa指针指向的对象,也就是判断传入的类对象的元类对象是否与传入的这个对象相等,所以这个cls应该是元类对象才有可能相等
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
判断传入的实例对象的类对象是否与传入的对象相等,所以cls只有可能是类对象才有可能相等
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
//循环判断传入的类对象的元类对象及其父类的元类对象是否等于传入的cls
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
//循环判断实例对象的父类的类对象是否等于传入的对象cls,也就是判断实例对象是否是cls及其子类的一种
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
通过这个两个方法的源码我们可以知道,isMemberOfClass:是检测方法调用者对象的类是否等于传入的这个类。isKindOfClass:是判断方法调用者对象的类是否等于传入的这个类或者其子类。还有一个适用于这四个方法的一点是,如果方法调用者是实例对象,那么传入的就应该是类对象;如果方法调用者是类对象,那么传入的就应该是元类对象。
所以回到上面的打印结果:
[[NSObject class] isMemberOfClass:[NSObject class]];
方法调用者是[NSObject class]也就是类对象,但是传入的参数也是类对象,所以很显然打印结果是0。
[[Person class] isKindOfClass:[Person class]];
方法的调用者是类对象,传入的参数也是类对象,所以打印结果是0。
[[Person class] isMemberOfClass:[Person class]];
方法调用者是类对象,传入的参数也是类对象,所以打印的是0。
[[NSObject class] isKindOfClass:[NSObject class]];
因为+ (BOOL)isKindOfClass:(Class)cls内部,先object_getClass((id)self)是先获得元类方法,判断其是否等于cls,然后沿着继承链取元类对象的superclass,我们想一下,当沿着继承链,一直不满足tcls == cls时,最终会找到NSObject类,取NSObject类的元类的superclass,而NSOBject的元类的superclass指向的是NSOBject类的类对象,所以打印输出是1。