iOS类的底层小结(结构、属性、方法等)

118 阅读4分钟

1. 前言

说起类,其实大家并不陌生,一个项目中,我们会创建很多类,在类里面也会定义很多成员变量、属性、方法等等,大家用的可以说是轻车熟路了。

不过我想大部分人在类在底层的实现并不是很了解,类在底层长什么样,我们定义的成员变量、属性,他们在底层储存在哪里呢?还有我们的方法、协议等等。

由于很多朋友已经写了大量的博客探索了这部分内容,本篇文章对于如何lldb进行探索调试不再讲解,而是写一些总结的内容,自我积累的同时也希望能帮到别人。

作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:812157648,不管你是小白还是大牛欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!

2. 类的结构

首先来看一下下面这段代码:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
        
        GYMPerson *person = [GYMPerson alloc];
        Class pClass = object_getClass(person);
    }
    return 0;
}

这段代码中,创建了一个person对象,然后通过下面的方法获取其Class

OBJC_EXPORT Class _Nullable
object_getClass(id _Nullable obj) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

那么这个Class在底层是如何定义的呢?

通过查阅源码,可以得到:

typedef struct objc_class *Class;

原来Class的原型则是objc_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();
    }
}

上面objc_class的代码省略了方法部分,我们主要看看其成员变量。这个类是个结构体,并且继承了objc_object,再继续看:

struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

由上面可知,底层的Class主要有四个属性:isa、superclass、cache和bits。

  1. isa:在之前的文章中已经讲解过,详见iOS底层isa探索分析。

  2. superclass:指针类型,记录了当前类的父类。

  3. cache:主要做了一些方法的缓存,方便方法的快速调用,这里不做详细讲解。

  4. bits:本篇文章重中之重,这里存储了很多信息,详见下面。

3. 类的属性方法等存储

由类的结构可知,在bits里面可能存储了一些相关信息,那么针对这个bits,我们在具体看一下。

class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }

bits的类中定义了一个很重要的方法,如上面,这里面返回了一个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;

    char *demangledName;
}

在这里面,我们看到methods properties protocols这些数组变量,难道对应的方法、属性以及协议都存储在这里了吗? 答案是存储到这里了,但并不是最原始的,因为开发过程中我们可以通过运行时动态的添加属性和方法等,而后加的这些确实会存储在class_rw_t里面。

在class_rw_t里面还有一个ro指针变量,看一下它的结构:

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;

    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};

在这个class_ro_t结构中,重点看一下ivars baseMethodList baseProtocols baseProperties.

baseProperties中存储了类中最原始的属性。

baseMethodList中存储了类中最原始的方法。

baseProtocols中存储了类中最原始的协议。

ivars则存储了类中的成员变量,包括由属性转过来带下划线的成员变量。

至于说类的类方法,类方法不会存储在本类中,而是存储在该类的元类中,元类和类的结构是一样的,探索方法也是一样的。

4. 总结

类在底层是一种struct结构,主要有个四个成员变量:

isa

superclass

cache

bits

类中所有的原始成员变量,属性,方法以及协议都存储在class_ro_t结构(俗称ro)中。同时会将ro中的成员变量,属性,方法以及协议赋值一份到class_rw_t(俗称rw)中,我们平时在运行时类中属性方法等的改动,也只是在修改rw中对应的属性方法等,不会影响到ro中的数据的。

还有类的类方法是存储在该类的元类中哦!

原文作者:Daniel_Coder

原文地址:blog.csdn.net/guoyongming…