runtime class isa
所有继承自 NSObject 的类实例化后的对象都会包含一个类型为 isa_t 的结构体。不只是实例会包含一个 isa 结构体,所有的类也有这么一个 isa
struct objc_object {
private:
isa_t isa;
...
}
最关键的就是isa_t这个类型和一系列的构造函数,点击查看isa_t发现这是一个联合体:
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
}
可以看到这个联合体里面有个Class类型的属性cls,看起来里面应该是关于这个对象的类的相关信息,那我们再看看Class包含了哪些内容。
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // 方法缓存
class_data_bits_t bits; //bits 里面存放这当前类属性,方法、协议等信息
...
}
由此可见Class是一个objc_class类型的结构体,而objc_class继承自objc_object,说明类也是一个对象,只是比普通的对象多了一些属性,比如superclass等。
既然类也是一个objc_object,那就是说类也有一个isa指针,那类的isa指针指向哪里呢?
在class之上还有叫做元类(meta class)的存在,而class的isa指针就是指向对应的meta class,
meta class的isa指向root meta class(绝大部分情况下是NSObject),root meta class的isa指针指向自己。class中存储的是描述对象的相关信息,那么相应的meta class中存放的就是描述class相关信息。说的更直白一点,在我们写代码时,通过对象来调用的方法(实例方法)都是存储在class中的,通过类名来调用的方法(类方法)都是存储在meta class中的
当实例方法被调用时,它要通过自己持有的 isa 来查找对应的类,然后在这里的 class_data_bits_t 结构体中查找对应方法的实现。同时,每一个 objc_class 也有一个指向自己的父类的指针 super_class 用来查找继承的方法。
实验:
TestObject *testObj = [TestObject new];
NSLog(@"%d", [testObj class] == [TestObject class]);
这个log会输出1(为true)
不好理解,看下具体源码,如下
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
object_getClass方法最终返回的是isa。所以TestObject调用class方法,返回的是自身;testObj调用class方法,返回的是isa指向的类,也是TestObject。
举一反三:
[self class] 与 [super class]
@implementation Son : Father
- (id)init
{
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end
眼熟,在哪里见过你,是的,很多有笔试经历小伙伴会碰到这题,看似很简单,但是被问为什么的时候?这么简单,居然问为什么,当时还是太嫩了,还是太年轻了。
详解:
NSStringFromClass([self class]) = Son
NSStringFromClass([super class]) = Son
self 是类的隐藏参数,指向当前调用方法的这个类的实例;
super 本质是一个编译器标示符,和 self 是指向的同一个消息接受者。不同点在于:super 会告诉编译器,当调用方法时,去调用父类的方法,而不是本类中的方法。
当使用 self 调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;而当使用 super 时,则从父类的方法列表中开始找。然后调用父类的这个方法。
在调用[super class]的时候,runtime会去调用objc_msgSendSuper方法,而不是objc_msgSend;
OBJC_EXPORT void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
/// Specifies the superclass of an instance.
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained id receiver;
/// Specifies the particular superclass of the instance to message.
#if !defined(__cplusplus) && !__OBJC2__
/* For compatibility with old objc-runtime.h header */
__unsafe_unretained Class class;
#else
__unsafe_unretained Class super_class;
#endif
/* super_class is the first class to search */
};
在objc_msgSendSuper方法中,第一个参数是一个objc_super的结构体,这个结构体里面有两个变量,一个是接收消息的receiver,一个是当前类的父类super_class。
objc_msgSendSuper的工作原理应该是这样的:
从objc_super结构体指向的superClass父类的方法列表开始查找selector,找到后以objc->receiver去调用父类的这个selector。注意,最后的调用者是objc->receiver,而不是super_class!
那么objc_msgSendSuper最后就转变成:
// 注意这里是从父类开始msgSend,而不是从本类开始
objc_msgSend(objc_super->receiver, @selector(class))
/// Specifies an instance of a class. 这是类的一个实例
__unsafe_unretained id receiver;
// 由于是实例调用,所以是减号方法
- (Class)class {
return object_getClass(self);
}
这里 其实就是 [self class] == [Son class] ,又回到之前讲过的isa知识了
由于找到了父类NSObject里面的class方法的IMP(一个函数指针,保存了方法的地址),又因为传入的入参objc_super->receiver = self。self就是son,调用class,所以父类的方法class执行IMP之后,输出还是son,最后输出两个都一样,都是输出son。