isa 和 Class

runtime版本主要分为OBJC和OBJC2,OBJC是传说,OBJC2是苹果2006年发布的,目前最新的。分析的源码是objc4-750
Runtime的数据结构主要包括objc_object、objc_class、isa指针、method_t

objc_object和objc_class
OBJC
struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */

具体可看runtime.h源码
上面是objc_class原始结构,官方是推荐使用Class替代objc_class的。

下面我们看下OBJC2的objc_class结构

OBJC2的objc_class结构
//objc_class 部分
typedef struct objc_class *Class;

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 
        return bits.data();
    }
   //...more
}

//objc-object部分
typedef struct objc_object *id;

struct objc_object {
private:
    isa_t isa;
}

union isa_t 
{
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }
    Class cls;
    uintptr_t bits;
}

//Object
@interface Object { 
    Class isa; 
}

//NSObject
@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}

具体可看objc-private.h
从上面源码可以看出:
Objective-C 对象都是 C 语言结构体实现的,objc_object结构体中包含一个isa_t类型的共用体。
平时我们使用的id类型,就是在Runtime中定义的objc_object类型。可以看出objc_object中包含isa_t共用体。
从继承关系,我们也可以看出Objective-C中类也是一个对象。

Cache_t

  • 用于快速查找方法执行函数
  • 是可增量扩展的哈希表结构。提高查找效率
  • 是局部性原理的最佳应用。把使用率最高的方法放到cache_t中。

class_data_bits_t
主要是对class_rw_t的封装。
class_rw_t

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;

    Class firstSubclass;
    Class nextSiblingClass;
}

类中的属性、方法、协议都保存在class_rw_t这里,以及只读信息class_ro_t。
class_ro_t

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
}

存储类在编译期就已经确定的属性、方法、协议。

类在内存中的位置是在编译期间决定的,在之后修改代码,也不会改变内存中的位置。
类的方法、属性以及协议在编译期间存放到了“错误”的位置,直到 realizeClass 执行之后,才放到了 class_rw_t 指向的只读区域 class_ro_t,这样我们即可以在运行时为 class_rw_t 添加方法,也不会影响类的只读结构。
在 class_ro_t 中的属性在运行期间就不能改变了,再添加方法时,会修改 class_rw_t 中的 methods 列表,而不是 class_ro_t 中的 baseMethods。

isa

从上面objc_class的结构可以看到,object和NSObject都包含isa指针。
isa指针的作用:

  • 当调用一个对象的实例方法时,会通过isa找到相应的类,然后在该- 类的class_data_bits_t中查找方法。同时,每一个 objc_class 也有一个指向自己的父类的指针 super_class 用来查找继承的方法。
  • 当调用类方法时,类对象的isa指针指向的是元类对象。(引入元类对象可以使实例方法和类方法的调用机制相同。)
    简单地说:
    实例方法调用时,通过对象的 isa 在类中获取方法的实现
    类方法调用时,通过类的 isa 在元类中获取方法的实现

下面,我们可以看下对象、类、元类之间的关系:


isa指针

类对象和元类对象都是objc_class数据结构的,objc_class继承自objc_object,都有isa指针。因此,实例对象可以通过isa指针找到类对象,类对象可以通过isa指针找到元类对象。

  • Root class (class)其实就是NSObject,NSObject是没有超类的,所以Root class(class)的superclass指向nil。
  • 每个Class都有一个isa指针指向唯一的Meta class
  • Root class(meta)的superclass指向Root class(class),也就是NSObject,形成一个回路。
  • 每个Meta class的isa指针都指向Root class (meta)。

类对象和元类对象的区别?

  • 类对象存储实例方法列表等信息
  • 元类对象存储类方法列表等信息

调用一个类方法,如果没有它的实现,但是有同名实例方法的实现,会不会崩溃,或者产生实际调用?
根据上面的关系图,会产生实际调用,根元类对象找不到对应的方法时,会去根类对象(即NSObject)中查找。

下面代码输出什么?

@implementation Son : Father
- (id)init
{
    self = [super init];
    if (self)
    {
        NSLog(@"%@", NSStringFromClass([self class]));
        NSLog(@"%@", NSStringFromClass([super class]));
    }
return self;
}
@end

都是Son

self和super的区别:

self是类的一个隐藏参数,每个方法的实现的第一个参数即为self。

super并不是隐藏参数,它实际上只是一个”编译器标示符”,它负责告诉编译器,当调用方法时,去调用父类的方法,而不是本类中的方法。

在调用[super class]的时候,runtime会去调用objc_msgSendSuper方法,而不是objc_msgSend,也不是[super_class class]。

objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
/// Specifies the superclass of an instance. 
struct objc_super {
    /// Specifies an instance of a class.
    __unsafe_unretained _Nonnull 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 _Nonnull Class class;
#else
    __unsafe_unretained _Nonnull Class super_class;
#endif
    /* super_class is the first class to search */
};

objc_msgSendSuper是去父类的方法列表中查找selector,找到后以receiver去调用父类中的selector。因此最后的调用者receiver就是self。

参考资料:
Runtime基础结构源码
Classes and Metaclasses

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容