02. iOS Class底层数据结构分析

231 阅读5分钟

在iOS中NSObject对象底层会转成 objc_class* 结构体指针类型, objc_class 结构体继承与 objc_object结构体, 内部有一个 isa_t isa 指针指向了结构体本身.

1. objc_object 结构


struct objc_object {
    Class isa;
};

struct objc_class : objc_object {
    Class isa; //继承与objc_object 如果是 实例对象指向当前类的 Class 实例, 如果是类对象 指向 metaClass
    Class superclass; //父类 Class 指针
    cache_t cache; // 缓存方法列表

    class_data_bits_t bits; // 存储了类的有关信息

    class_rw_t *data() const { // 类里边可读可写的信息 属于dirty memory, 还有一个 class_ro_t 只读信息 属于clean memory
        return bits.data();
    }
};

2. class_data_bits_t结构 可读可写类型 dirty memory

#define FAST_DATA_MASK          0x00007ffffffffff8UL
struct class_data_bits_t {
    friend objc_class;

    // Values are the FAST_ flags above.
    // uintptr_t能够存储指针的无符号整数类型 通过 & FAST_DATA_MASK 拿到 class_rw_t信息
    uintptr_t bits;
public:
    // 类或者对象信息在这个 data 列表存储
    class_rw_t* data() const {
        class_rw_t *rw_t = (class_rw_t *)(bits & FAST_DATA_MASK);
        return rw_t;
    }
};

3. class_rw_tclass_ro_t结构 结构

struct class_rw_t { 
    const class_ro_t *ro; //存储只读类信息
    method_array_t methods; // 方法列表
    property_array_t properties;  //属性列表
    protocol_array_t protocols; //协议信息
    char *demangledName; //类名
};
  • class_ro_t结构 只读类型 clean memory
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; //类名 MJPerson
    method_list_t * baseMethodList;  //方法列表
    protocol_list_t * baseProtocols; // 协议信息
    const ivar_list_t * ivars; // 成员列表
    const uint8_t * weakIvarLayout; 
    property_list_t *baseProperties;  //属性列表
};

class_rw_t提供了运行时对类扩展的能力,class_ro_t存储的是类在编译时就确定的一些信息。 将一些稳定不改动的数据放到class_ro_t 可以提高程序读取效率减少内存使用. 具体可参考 wwdc2020 developer.apple.com/videos/play…

4. 打印methods里边信息

由于网上大多数教程都是通过指针内存便宜 先拿到bits->data(), 再通过 bits->data()拿到 methods, 然后一点点获取内存 方法太过于繁琐 不如直接打印只管.

自己写了个get_methods()函数 打印所有方法信息, 如果是object类型打印出来实例方法(-), 如果是 class 类型打印出来类方法(+)

    #封装了方法参数信息
    @interface MJMethod_T : NSObject
    @property (nonatomic, assign) IMP impl;
    @property (nonatomic, assign) SEL selector;
    @property (nonatomic,copy) NSString *name;
    @property (nonatomic,copy) NSString *types;
    @end
    
    /// 获取所有方法列表
    NSArray<MJMethod_T *> * get_methods() {
        NSMutableArray *array = [NSMutableArray array];
        auto const methods = this->methods();
        for (const auto &meth2: methods) {
            MJMethod_T *method = [[MJMethod_T alloc] init];
            method.selector = meth2.big().name;
            method.impl = meth2.big().imp;
            method.types = [NSString stringWithCString:meth2.big().types encoding:NSUTF8StringEncoding];
            method.name = NSStringFromSelector(meth2.big().name);
            [array addObject:method];
        }
        return array;
    }

控制台 log 打印方法信息

studentClassData -> (
    "name = doSomething, types = v16@0:8, impl = 0x1091011a0",
    "name = studentInstanceMethod, types = v16@0:8, impl = 0x109101180",
    "name = height, types = i16@0:8, impl = 0x1091011c0",
    "name = setHeight:, types = v20@0:8i16, impl = 0x1091011e0",
    "name = test, types = v16@0:8, impl = 0x109101170"
)

5. 打印properties属性列表信息 property类型的属性都可以打印出来

    /// 获取所有属性列表
    NSArray <NSDictionary *> *get_properties() {
        const property_array_t properties = this->properties();
        if (properties.count() == 0) {
            return @[];
        }
        NSMutableArray *array = [NSMutableArray array];
        for (auto& prop : properties) {
            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
            NSString *name = [NSString stringWithUTF8String:prop.name];
            NSString *attributes = [NSString stringWithUTF8String:prop.attributes];
            dict[@"name"] = name ?: @"";
            dict[@"attributes"] = attributes ?: @"";
            [array addObject:dict];
        }
        return array;
    }

控制台 log 打印 properties信息

6. 打印protocols信息

    #打印所有协议信息
    NSArray <NSDictionary *> * get_protocols() {
        NSMutableArray *array = [NSMutableArray array];
        const protocol_array_t protocols = this->protocols();
        for (const auto& proto : protocols) {
            NSMutableDictionary *hashMap = [NSMutableDictionary dictionary];
            protocol_t p_t = *((protocol_t *)proto);
            hashMap[@"isa"] = @{@"object": NSStringFromClass(p_t.isa)};
            NSString *mangledName = [NSString stringWithUTF8String:p_t.mangledName];
            hashMap[@"mangledName"] = mangledName;
            method_list_t *instanceMethods = p_t.instanceMethods;
            if (instanceMethods != NULL || instanceMethods != nullptr) {
                hashMap[@"instanceMethods"] = __method_list_to_array(instanceMethods);
            }  else {
                hashMap[@"instanceMethods"] = [NSNull null];
            }

            method_list_t *classMethods = p_t.classMethods;
            if (classMethods != NULL || classMethods != nullptr) {
                hashMap[@"classMethods"] = __method_list_to_array(classMethods);
            }  else {
                hashMap[@"classMethods"] = [NSNull null];
            }

            method_list_t *optionalInstanceMethods = p_t.optionalInstanceMethods;
            if (optionalInstanceMethods != NULL || optionalInstanceMethods != nullptr) {
                hashMap[@"optionalInstanceMethods"] = __method_list_to_array(optionalInstanceMethods);
            } else {
                hashMap[@"optionalInstanceMethods"] = [NSNull null];
            }

            method_list_t *optionalClassMethods = p_t.optionalClassMethods;
            if (optionalClassMethods != NULL || optionalClassMethods != nullptr) {
                hashMap[@"optionalClassMethods"] = __method_list_to_array(optionalClassMethods);
            } else {
                hashMap[@"optionalClassMethods"] = [NSNull null];
            }

            hashMap[@"size"] = @(p_t.size);
            hashMap[@"flags"] = @(p_t.flags);

            struct protocol_list_t *protocols_list = p_t.protocols;
            if (protocols_list != NULL || protocols_list != nullptr) {
                NSMutableArray *list_arr = [NSMutableArray array];
                for (const auto&meth_t: *protocols_list) {
                    [list_arr addObject:__protocol_list_t_to_array(meth_t)];
                }
                hashMap[@"protocols"] = list_arr;
            } else {
                hashMap[@"protocols"] = [NSNull null];
            }

            hashMap[@"_extendedMethodTypes"] = [NSString stringWithFormat:@"%p",p_t._extendedMethodTypes];
            const char* _demangledName = p_t._demangledName;
            if (_demangledName != NULL) {
                hashMap[@"_extendedMethodTypes"] = [NSString stringWithUTF8String:_demangledName];
            } else {
                hashMap[@"_extendedMethodTypes"] = [NSNull null];
            }

            property_list_t *_classProperties = p_t._classProperties;
            if (_classProperties != NULL) {
                hashMap[@"_extendedMethodTypes"] = @[];
            } else {
                hashMap[@"_extendedMethodTypes"] = [NSNull null];
            }

            [array addObject:hashMap];
        }
        return array;
    }
    #打印协议中的方法信息
    NSArray *__method_list_to_array(method_list_t *instanceMethods) {
        NSMutableArray *array = [NSMutableArray array];
        for (const auto&meth_t: *instanceMethods) {
            NSMutableDictionary *hashmap = [[NSMutableDictionary alloc] init];
            const struct method_t::big _b = meth_t.big();
            hashmap[@"sel"] = NSStringFromSelector(_b.name);
            hashmap[@"types"] = [NSString stringWithUTF8String:_b.types];
            hashmap[@"imp"] = [NSString stringWithFormat:@"%p",_b.imp];
            [array addObject:hashmap];
        }
        return [array copy];
    }
    #打印协议中继承的 protocol 比如 MJProtocol: NSObject协议 
    NSDictionary *__protocol_list_t_to_array(protocol_ref_t meth_t) {
        NSMutableDictionary *hashMap = [NSMutableDictionary dictionary];
        protocol_t p_t = *((protocol_t *)meth_t);
        if (p_t.isa != nullptr) {
            hashMap[@"isa"] = @{@"object": NSStringFromClass(p_t.isa)};
        }
        NSString *mangledName = [NSString stringWithUTF8String:p_t.mangledName];
        hashMap[@"mangledName"] = mangledName;
        method_list_t *instanceMethods = p_t.instanceMethods;
        if (instanceMethods != NULL || instanceMethods != nullptr) {
            hashMap[@"instanceMethods"] = __method_list_to_array(instanceMethods);
        }

        method_list_t *classMethods = p_t.classMethods;
        if (classMethods != NULL || classMethods != nullptr) {
            hashMap[@"classMethods"] = __method_list_to_array(classMethods);
        }


        method_list_t *optionalInstanceMethods = p_t.optionalInstanceMethods;
        if (optionalInstanceMethods != NULL || optionalInstanceMethods != nullptr) {
            hashMap[@"optionalInstanceMethods"] = __method_list_to_array(optionalInstanceMethods);
        }

        method_list_t *optionalClassMethods = p_t.optionalClassMethods;
        if (optionalClassMethods != NULL || optionalClassMethods != nullptr) {
            hashMap[@"optionalClassMethods"] = __method_list_to_array(optionalClassMethods);
        }
        hashMap[@"size"] = @(p_t.size);
        hashMap[@"flags"] = @(p_t.flags);
        return hashMap;
    }

控制台输出protocols信息

(
        {
        "_extendedMethodTypes" = "<null>";
        classMethods = "<null>";
        flags = 0;
        instanceMethods = "<null>";
        isa =         {
            object = Protocol;
        };
        mangledName = MJProtocol;
        optionalClassMethods =         (
                        {
                imp = 0x0;
                sel = doSomething2;
                types = "v16@0:8";
            }
        );
        optionalInstanceMethods =         (
                        {
                imp = 0x0;
                sel = doSomething;
                types = "v16@0:8";
            }
        );
        protocols =         (
                        {
                flags = 0;
                instanceMethods =                 (
                                        {
                        imp = 0x0;
                        sel = "isEqual:";
                        types = "B24@0:8@16";
                    },
                    ..... 还有很多不再一一展示........
                      {
                        imp = 0x0;
                        sel = description;
                        types = "@16@0:8";
                    }
                );
                mangledName = NSObject;
                optionalInstanceMethods =   (
                                        {
                        imp = 0x0;
                        sel = debugDescription;
                        types = "@16@0:8";
                    }
                );
                size = 96;
            }
        );
        size = 96;
    }
)

7. 打印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

    union {
        const uint8_t * ivarLayout;
        Class nonMetaclass;
    };

    explicit_atomic<const char *> name; //类名称
    void *baseMethodList; // 方法列表 这里的方法都是类方法
    protocol_list_t * baseProtocols; // 协议列表
    const ivar_list_t * ivars; //成员列表

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties; // 属性列表
}

get_ro_t_info方法

/// 获取class_ro_t信息
    NSDictionary * get_ro_t_info() {
        const class_ro_t *ro_ptr = ro();
        NSMutableDictionary *hashMap = [NSMutableDictionary dictionary];
        [hashMap setObject:@(ro_ptr->flags) forKey:@"flags"];
        [hashMap setObject:@(ro_ptr->instanceSize) forKey:@"instanceSize"];
        [hashMap setObject:@(ro_ptr->instanceStart) forKey:@"instanceStart"];
        [hashMap setObject:@(ro_ptr->reserved) forKey:@"reserved"];
        [hashMap setObject:[NSString stringWithCString:ro_ptr->getName() encoding:NSUTF8StringEncoding] forKey:@"name"];

        NSMutableArray *baseMethods = [NSMutableArray array];
        for (const auto& meth2 : *ro_ptr->baseMethods()) {
            MJMethod_T *method = [[MJMethod_T alloc] init];
            method.selector = meth2.big().name;
            method.impl = meth2.big().imp;
            method.types = [NSString stringWithCString:meth2.big().types encoding:NSUTF8StringEncoding];
            method.name = NSStringFromSelector(meth2.big().name);
            [baseMethods addObject:method];
        }
        hashMap[@"baseMethods"] = baseMethods;
        NSMutableArray *baseProperties = [NSMutableArray array];
        for (auto& prop : *ro_ptr->baseProperties) {
            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
            NSString *name = [NSString stringWithUTF8String:prop.name];
            NSString *attributes = [NSString stringWithUTF8String:prop.attributes];
            dict[@"name"] = name ?: @"";
            dict[@"attributes"] = attributes ?: @"";
            [baseProperties addObject:dict];
        }
        hashMap[@"baseProperties"] = baseProperties;
        hashMap[@"ivars"] = get_ivars();

        protocol_list_t * baseProtocols = ro_ptr->baseProtocols;
        if (baseProtocols != nullptr) {
            NSMutableArray *list_arr = [NSMutableArray array];
            for (const auto&meth_t: *baseProtocols) {
                [list_arr addObject:__protocol_list_t_to_array(meth_t)];
            }
            hashMap[@"baseProtocols"] = list_arr;
        } else {
            hashMap[@"baseProtocols"] = @"nil";
        }
        return [hashMap copy];
    }

log输出 class_ro_t信息

{
    baseMethods =     (
        "name = doSomething, types = v16@0:8, impl = 0x108bd7430",
        "name = studentInstanceMethod, types = v16@0:8, impl = 0x108bd7410",
        "name = height, types = i16@0:8, impl = 0x108bd7450",
        "name = setHeight:, types = v20@0:8i16, impl = 0x108bd7470",
        "name = test, types = v16@0:8, impl = 0x108bd7400"
    );
    baseProperties =     (
                {
            attributes = "Ti,N,V_height";
            name = height;
        },
                {
            attributes = "TQ,R";
            name = hash;
        },
                {
            attributes = "T#,R";
            name = superclass;
        },
                {
            attributes = "T@\"NSString\",R,C";
            name = description;
        },
                {
            attributes = "T@\"NSString\",R,C";
            name = debugDescription;
        }
    );
    baseProtocols =     (
                {
            flags = 0;
            isa =             {
                object = Protocol;
            };
            mangledName = MJProtocol;
            optionalClassMethods =             (
                                {
                    imp = 0x0;
                    sel = doSomething2;
                    types = "v16@0:8";
                }
            );
            optionalInstanceMethods =             (
                                {
                    imp = 0x0;
                    sel = doSomething;
                    types = "v16@0:8";
                }
            );
            size = 96;
        }
    );
    flags = 128;
    instanceSize = 24;
    instanceStart = 16;
    ivars =     (
                {
            alignment = 2;
            name = "_weight";
            offset = 16;
            size = 4;
            type = i;
        },
                {
            alignment = 2;
            name = "_height";
            offset = 20;
            size = 4;
            type = i;
        }
    );
    name = MJStudent;
    reserved = 0;
}

8. 总结: objc_class内部大致结构体如下图

image.png