iOS底层探索之类结构下篇

上篇分析类结构,获取到了属性列表 property_array_t 和方法列表 method_array_t

实践代码:

@interface GLPerson : NSObject

@property (nonatomic, strong) NSString *name; /**<  8个字节  */
@property (nonatomic, strong) NSString *nickName; /**<  8个字节  */

- (void)sayHello;

+ (void)goSchool;

@end

@interface GLStudent : GLPerson

@end

一、类的类方法存储

通过源码查看,没有看到底层把 method 区分类方法还是实例方法。既然实例方法存在类对象中,类方法是不是可能存在元类中。试着通过 lldb 打印一下。

(lldb) x/4gx GLPerson.class
0x1000022b8: 0x0000000100002290 0x0000000100333140
0x1000022c8: 0x000000010113f640 0x0001802400000003
(lldb) p 0x0000000100002290 & 0x00007ffffffffff8ULL // 通过isa找到元类
(unsigned long long) $1 = 4294976144
(lldb) x/4gx $1  // 读取元类的内存
0x100002290: 0x00000001003330f0 0x00000001003330f0
0x1000022a0: 0x0000000100643250 0x0001e03500000007
(lldb) p (class_data_bits_t *)0x1000022b0 // 通过偏移32字节获取class_data_bits_t指针
(class_data_bits_t *) $2 = 0x00000001000022b0
(lldb) p *$2->data() // 调用class_data_bits_t 的data方法获取class_rw_t
(class_rw_t) $3 = {
  flags = 2684878849
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = 4294975528
  }
  firstSubclass = 0x00000001000022e0
  nextSiblingClass = 0x00007fff88bf1948
}
(lldb) p $3.methods() // 打印元类的方法列表
(const method_array_t) $4 = {
  list_array_tt<method_t, method_list_t> = {
     = {
      list = 0x0000000100002070
      arrayAndFlag = 4294975600
    }
  }
}
(lldb) p $4.list
(method_list_t *const) $5 = 0x0000000100002070
(lldb) p *$5
(method_list_t) $6 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 1
    first = {
      name = "goSchool"
      types = 0x0000000100000f7c "v16@0:8"
      imp = 0x0000000100000d70 (`+[GLPerson goSchool])
    }
  }
}
(lldb) p $6.get(0) // 第一个就是声明的类方法
(method_t) $7 = {
  name = "goSchool"
  types = 0x0000000100000f7c "v16@0:8"
  imp = 0x0000000100000d70 (`+[GLPerson goSchool])
}
(lldb) p $6.get(1)  // 只有一个类方法,下一个越界了
Assertion failed: (i < count), function get, file /Users/xulong/Desktop/学习/alloc流程分析/runtime/objc-runtime-new.h, line 438.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.

通过上面代码发现:

  • 方法存储的时候是没有明确的区分是类方法还是实例方法的
  • 实例方法存储在类的数据中;
  • 类方法存储在元类的数据中;
1.1 通过runtime源码分析

runtime 里面有方法:

  • class_getInstanceMethod : 返回一个类的指定实例方法;
  • class_getClassMethod :返回一个类的指定类方法;
        const char *className = class_getName([GLPerson class]);
        Class metaClass = objc_getMetaClass(className);
        
        Method instanceM1 = class_getInstanceMethod([GLPerson class], @selector(sayHello));
        Method instanceM2 = class_getInstanceMethod([GLPerson class], @selector(goSchool));

        Method classM1 = class_getClassMethod([GLPerson class], @selector(sayHello));
        Method classM2 = class_getClassMethod([GLPerson class], @selector(goSchool));
           
        Method metaInstanceM1 = class_getInstanceMethod(metaClass, @selector(sayHello));
        Method metaInstanceM2 = class_getInstanceMethod(metaClass, @selector(goSchool));
        
        Method metaClassM1 = class_getClassMethod(metaClass, @selector(sayHello));
        Method metaClassM2 = class_getClassMethod(metaClass, @selector(goSchool));
        
        
        NSLog(@"instanceM1:%p; instanceM2:%p; classM1:%p; classM2:%p;", instanceM1, instanceM2, classM1, classM2);
        
        NSLog(@"metaInstanceM1:%p; metaInstanceM2:%p; metaClassM1:%p; metaClassM2:%p;", metaInstanceM1, metaInstanceM2, metaClassM1, metaClassM2);

console: instanceM1:0x1000020f8; instanceM2:0x0; classM1:0x0; classM2:0x100002090;
console: metaInstanceM1:0x0; metaInstanceM2:0x1000020a0; metaClassM1:0x0; metaClassM2:0x1000020a0;

分析一下
第一行类输出:代表类有无

  • instanceM1 有值: 有 sayHello 实例方法,instanceM2 没值: 没有 goSchool 实例方法;
  • classM1 没值:没有 sayHello 类方法,classM2 有值:有 goSchool 类方法;

第二行元类输出:代表元类有无

  • metaInstanceM1 没值: 没有 sayHello 实例方法,metaInstanceM2 有值: 有 goSchool 实例方法;
  • metaClassM1 没值:没有 sayHello 类方法,metaClassM2 有值:有 goSchool 类方法;

这里先看下 class_getClassMethod 的源码:

Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    return class_getInstanceMethod(cls->getMeta(), sel);
}

---
    // NOT identical to this->ISA when this is a metaclass
    Class getMeta() {
        if (isMetaClass()) return (Class)this;
        else return this->ISA();
    }

class_getClassMethod 的流程:

  1. class_getClassMethod 里面调用的也是 class_getInstanceMethod;
  2. 只不过传的类是 cls->getMeta() 这个方法返回的;
  3. getMeta 如果是元类,返回的就是自己;
  4. getMeta 如果不是元类,返回的是 ISA();
  5. 类的 ISA() 返回的是 该类的元类;

class_getClassMethod总结

  • 如果传的类,里面是获取的元类的 class_getInstanceMethod 方法。也既类方法是从元类里面去找的。
  • 如果传的是元类,直接用元类调用 class_getInstanceMethod 方法。

二、是不是这个类 isKindOfClass & isMemberOfClass

首先有这样一段代码:

        BOOL obClass_k = [(id)[NSObject class] isKindOfClass:[NSObject class]];       //
        BOOL obClass_m = [(id)[NSObject class] isMemberOfClass:[NSObject class]];     //
        BOOL peClass_k = [(id)[GLPerson class] isKindOfClass:[GLPerson class]];       //
        BOOL peClass_m = [(id)[GLPerson class] isMemberOfClass:[GLPerson class]];     //
        NSLog(@"\n obClass_k :%hhd\n obClass_m :%hhd\n peClass_k :%hhd\n peClass_m :%hhd\n",obClass_k, obClass_m, peClass_k, peClass_m);

        BOOL obInstance_k = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];       //
        BOOL obInstance_m = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];     //
        BOOL peInstance_k = [(id)[GLPerson alloc] isKindOfClass:[GLPerson class]];       //
        BOOL peInstance_m = [(id)[GLPerson alloc] isMemberOfClass:[GLPerson class]];     //
        NSLog(@"\n obInstance_k :%hhd\n obInstance_m :%hhd\n peInstance_k :%hhd\n peInstance_m :%hhd\n",obInstance_k, obInstance_m, peInstance_k, peInstance_m);

console:

 obClass_k :1
 obClass_m :0
 peClass_k :0
 peClass_m :0
---
 obInstance_k :1
 obInstance_m :1
 peInstance_k :1
 peInstance_m :1

可找到上面调用的具体源码如下:

// 类方法调用
+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}
// 实例方法调用
- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}

但是在实际调用的时候,断点发现 isKindOfClass 没有走到 上面的源码中,通过汇编查看下具体走的什么方法

isKindOfClass汇编查看.png

发现调用的是objc_opt_isKindOfClasswhat !!!,猜测应该是苹果再 llvm 编译时期做了操作。

// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
    if (slowpath(!obj)) return NO;
    Class cls = obj->getIsa();
    if (fastpath(!cls->hasCustomCore())) {
        for (Class tcls = cls; tcls; tcls = tcls->superclass) {
            if (tcls == otherClass) return YES;
        }
        return NO;
    }
#endif
    return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
  • 分析1 [(id)[NSObject class] isKindOfClass:[NSObject class]];

    1. [NSObject class] 返回类自己;
    2. objc_opt_isKindOfClass 中,objNSObject 类, otherClassNSObject 类;
    3. Class cls 首先赋值为 NSObject 的元类;
    4. Class tcls = clstcls 赋值元类,判断 tcls 是否等于 otherClass,类不等于元类;
    5. tcls = tcls->superclassNSObject 元类的 superclass 为根类,也就是 [NSObject class]
    6. tcls 现在为 NSObject 类,所以和 otherClass 相等;
    7. 返回 YES
  • 分析2 [(id)[NSObject class] isMemberOfClass:[NSObject class]]:

    1. [NSObject class] 返回类自己,所以会调用类方法;
    2. selfcls 都为 NSObject 类;
    3. self->ISA() 返回 NSObject 的元类;
    4. NSObject 的元类和类不相等;
    5. 返回 NO
  • 分析3 [(id)[GLPerson class] isKindOfClass:[GLPerson class]]:

    1. [GLPerson class] 返回类自己,所以会调用类方法;
    2. objc_opt_isKindOfClass 中,objGLPerson 类, otherClassGLPerson 类;
    3. Class clsClass tcls 首先赋值为 GLPerson 的元类;
    4. tcls 存在,判断 tcls 是否等于 otherClass,类不等于元类;
    5. tcls = tcls->superclassGLPerson 元类的 superclass 为根元类,也就是 [NSObject class] 的元类;
    6. tcls 现在为 NSObject 元类,不等于 otherClassGLPerson 类;
    7. 再次把 tcls = tcls->superclassNSObject 元类的 superclass[NSObject class] 类;
    8. tclsNSObject 类 不等于 otherClassGLPerson 类;
    9. 再再次 tcls = tcls->superclassNSObject 类的 superclassnil
    10. tclsnil;
    11. 返回 NO
  • 分析4 [(id)[GLPerson class] isMemberOfClass:[GLPerson class]]:

    1. [GLPerson class] 返回类自己,所以会调用类方法;
    2. selfcls 都为 GLPerson 类;
    3. self->ISA() 返回 GLPerson 的元类;
    4. GLPerson 的元类和类不相等;
    5. 返回 NO
  • 分析5 [(id)[NSObject alloc] isKindOfClass:[NSObject class]]:

    1. [NSObject alloc] 是实例;
    2. Class cls = obj->getIsa()cls 赋值为 NSObject 类;
    3. Class clstcls 赋值为 NSObject 类,otherClass 赋值为 NSObject 类;cls[NSObject class]
    4. 所以 tcls 等于 otherClass,都是 NSObject 类;
    5. 返回 YES;
  • 分析6 [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]:

    1. [NSObject alloc] 是实例,所以会调用实例方法;
    2. [self class] 为 NSObject 类,cls[NSObject class]
    3. 都是 NSObject 类;
    4. 返回 YES;
  • 分析7 [(id)[GLPerson alloc] isKindOfClass:[GLPerson class]]:

    1. [GLPerson alloc] 是实例;
    2. Class cls = obj->getIsa()cls 赋值为 GLPerson 类;
    3. Class clstcls 赋值为 GLPerson 类,otherClassGLPerson 类;
    4. 所以 tcls 等于 otherClass,都是 GLPerson 类;
    5. 返回 YES;
  • 分析8 [(id)[GLPerson alloc] isMemberOfClass:[GLPerson class]]:

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