iOS底层探索

iOS底层探索之类结构下篇

2020-09-16  本文已影响0人  loongod

上篇分析类结构,获取到了属性列表 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 里面有方法:

        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;

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

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

这里先看下 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总结

二、是不是这个类 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);
}
上一篇 下一篇

猜你喜欢

热点阅读