iOS runtime原理class isa

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。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容