iOS runtime合集

iOS底层探索 -- 类的结构分析

2020-09-14  本文已影响0人  iOS小木偶
isa流程图.png

之前研究了isa的本质,在研究过程中经常会看到这张isa指针流程和类继承的关系图 今天我们就顺着这张图开始研究

关于类 元类 根源类

举例说明:FQPerson继承于NSObjectFQTeacher继承于FQPerson

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        
        NSObject *obj = [[NSObject alloc] init];
        FQPerson * person = [FQPerson alloc];
        FQTeacher * teacher = [FQTeacher alloc];
        
        NSLog(@"person:%@  teacher:%@",person,teacher);
    }
    return 0;
}

由lldb断点调试

obj的isa研究.jpg
在我们尝试对NSObject的对象objisa追踪时,我们发现一个有趣现象,我们尝试打印了几个不同的地址,但是打印出来的结果均为NSObject
这是否代表一个类有多个类地址?
对此,我们做另一个尝试
person的isa分析.jpg
我们通过不同的方式获取了FQPerson的类地址,但是结果是相同的,所以证明一个类只有一个类地址
那么为什么上面的NSObject为何有多个地址呢?
其实就是最初继承关系图中的元类根元类
同时打印person信息验证
person的isa追踪.jpg

objc_class 和 objc_object

我们知道类的底层是objc_class 而对象是以objc_object为模板

通过源码分析

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() const {
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

objc_class是一个继承于objc_object
objc_object中含有一个isa

这就是为什么所有对象都有isa属性

同时也可以说万物皆对象


内存偏移

在我们对类对象分析之前,我们开始了解一个概念 - 内存偏移

int c[4] = {1, 2, 3, 4};
int *d = c;
NSLog(@"%p -- %p -- %p", &c, &c[0], &c[1]);
NSLog(@"%p -- %p -- %p", d, d+1, d+2);

打印结果
0x7ffeefbff530 -- 0x7ffeefbff530 -- 0x7ffeefbff534
0x7ffeefbff530 -- 0x7ffeefbff534 -- 0x7ffeefbff538

由于数据内存中的存储是连续的,所以我们在知道偏移量的情况下,我们可以直接通过内存地址获取数据。

所以如果我们想分析class_data_bits_t的内部数据,我们首先需要知道cache_t的大小

通过对cache_t的源码分析,我们可以计算出大小为8+4+2+2 = 16字节,
同时加上 isasuperclass 的各8字节,class_data_bits_t 应当从首地址的后32字节开始


类的结构分析

通过上面的内存偏移的方法,我们可以尝试获取class_data_bits_t中的数据

20200914002733.jpg
可以发现,我们已经成功获取到了class_data_bits_t的信息
struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint16_t witness;
#if SUPPORT_INDEXED_ISA
    uint16_t index;
#endif




    class_rw_ext_t *extAlloc(const class_ro_t *ro, bool deep = false);

public:


    const method_array_t methods() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>()->methods;
        } else {
            return method_array_t{v.get<const class_ro_t *>()->baseMethods()};
        }
    }

    const property_array_t properties() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>()->properties;
        } else {
            return property_array_t{v.get<const class_ro_t *>()->baseProperties};
        }
    }

    const protocol_array_t protocols() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>()->protocols;
        } else {
            return protocol_array_t{v.get<const class_ro_t *>()->baseProtocols};
        }
    }
};

对类来说,最重要的是属性和方法,我们尝试下继续获取属性和方法

属性打印.jpg
打印方法.jpg

总结

类的本质是一个结构体对象,而结构体对象可以通过属性地址来分析。

上一篇 下一篇

猜你喜欢

热点阅读