前语

这篇文章起源于很陈旧的一个面试题,为什么一个类的 [self class][super class] 的输出是相同的,今天从源码的视点去剖析一下。

场景再现:

@interface Animal : NSObject
@end
@implementation Animal
@end
@interface Cat : Animal
@end
@implementation Cat
- (instancetype)init {
  if (self = [super init]) {
    NSLog(@"%@", [self class]);
    NSLog(@"%@", [super class]);
  }
  return self;
} 
@end

输出:

Cat
Cat

源码

既然都是调用的 class 办法,咱们就先看一下 class 办法的完成是什么:

- (Class)class {
  return object_getClass(self);
}
Class object_getClass(id obj)
{
  if (obj) return obj->getIsa();
  else return Nil;
}

能够发现,class 办法其实便是传入一个目标(实例或类),然后输出这个目标的 isa,那么关键就在于,[self class][super class] 所传入的目标分别是什么。

Cat 放到一个 .m 文件中,进入到它所在的文件夹,履行 clang -rewrite-objc Cat.m,得到一个 Cat.cpp 文件,这是 Clang 编译 Cat.m 的输出文件,打开这个文件,直接查找 Cat_init,就能定位到咱们在 Cat.m 中编写的 init 办法。

去掉干扰项后,[self class][super class] 的源码如下:

// [self class]
((Class (*)(id, SEL))(void *)objc_msgSend)(
    (id)self,  // Cat
    sel_registerName("class") // class 办法
)
// [super class]
((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)(
    (__rw_objc_super) {
        (id)self,  // Cat
        (id)class_getSuperclass(objc_getClass("Cat")) // Animal
        },
    sel_registerName("class") // class 办法
 )

能够看到它们的不同在于,[self class] 调用的是 objc_msgSend,第一个参数为 (id)self,而 [super class] 调用的是 objc_msgSendSuper,第一个参数是 __rw_objc_super,两者的第二个参数都是相同的,都是办法名,咱们先疏忽,只看不同的部分。

__rw_objc_super 的构造如下:

struct __rw_objc_super {
	struct objc_object *object; 
	struct objc_object *superClass; 
	__rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {} 
};

查看了一下 objc_msgSendSuper 的源码(只有头文件),它的界说为:

objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)

接收的第一个参数是 objc_super 类型的一个结构体,与咱们编译出来的 __rw_objc_super 其实是同一个东西,咱们看一下源码中的 objc_super

struct objc_super {
  __unsafe_unretained _Nonnull id receiver;
#if !defined(__cplusplus) && !__OBJC2__
  __unsafe_unretained _Nonnull Class class;
#else
  __unsafe_unretained _Nonnull Class super_class;
#endif
};
// 因为 #if 中的条件不满足,所以简化下来,它的结构为:
struct objc_super {
  id receiver;  
  Class super_class;
};

这个与 __rw_objc_super 的结构是对应的。

一个 receiver,一个 super_class,根据咱们编译后看到的,receiver 传入的是 self,也便是 Cat 实例目标,super_class 用的是 class_getSuperclass(objc_getClass("Cat")) 得出来的父类,也便是 Animal

关键点在于,这里传入的 receiver,依然是 Cat 的实例目标,在调用 class 办法,也便是 object_getClass,传入的参数便是 receiver,其实便是 self,所以回来的 class,便是 Cat 类。

也便是,[self class][super class],二者调用 object_getCalss 办法所传入的参数,其实都是 self,所以它们的回来结果是相同的。

而它们的区别在于查找办法的顺序上,objc_msgSend 是从自己开端,沿着承继链一路往上去查找办法,而 objc_msgSendSuper 是从 self.superclass,也便是自己的父类开端,沿着承继链一路往上查找办法,但是用于消息发送的 receiver,接收者仍是自己,只不过不会查找自己的类中的办法列表,而直接从自己的父类中开端查找。

就比如有两个快递,一个是我买的,一个是我爸买的,但是用的都是我的信息,我是快递的签收人,我知道有一个快递是我爸的,我会给他,但是如果有人问,签收人的信息,那就仍是我。

Always me.

其他

runtime 是开源的,能够从 官网 进行下载,或者网上找一份能够编译的版别,我用的是 KCCObjc4。