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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容