【iOS面试#2】类结构的简单分析

97 阅读3分钟

1. 整体结构

image.png

类结构分析.png

2. 知识点

1). 成员变量存放在哪

  • 成员变量(1. 在 { } 中定义的变量,不论是 .h 还是 .m中;2. 属性生成的),可以通过 class -> bits -> data() -> ro() -> ivars获取

2). 属性存放在哪

  • 属性(使用 @property定义的),可以通过 class -> bits -> data() -> properties() -> list获取属性列表,只包含属性,不包含成员变量

3). 类的实例方法存放在哪

  • 类的实例方法可以通过 class -> bits -> data() -> methods() -> list获取方法列表,除了实例方法和属性的 getset方法之外,还有一个c++ 的 .cxx_destruct方法

4). 类的类方法存放在哪

  • 类的类方法,需要通过 isa找到 metaClass,然后通过 metaClass -> bits -> data() -> methods()获取

5). 类的协议存放在哪

  • 类的协议可以通过 class -> bits -> data() -> protocols() -> list获取协议列表

3. 结构分析

1). Class - objc_object - id

objc.h定义,从#if !OBJC_TYPES_DEFINED可以看出,使用的不是这个

#if !OBJC_TYPES_DEFINED

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;

#endif

objc-private.h定义,实际上使用的是这个

struct objc_object {

private:
    isa_t isa;
    
public:
    // ISA() assumes this is NOT a tagged pointer object
    Class ISA(bool authenticated = false);
    
    // rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA
    Class rawISA();

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();

    uintptr_t isaBits() const;

    // initIsa() should be used to init the isa of new objects only.
    // If this object already has an isa, use changeIsa() for correctness.
    // initInstanceIsa(): objects with no custom RR/AWZ
    // initClassIsa(): class objects
    // initProtocolIsa(): protocol objects
    // initIsa(): other objects
    void initIsa(Class cls /*nonpointer=false*/);
    void initClassIsa(Class cls /*nonpointer=maybe*/);
    void initProtocolIsa(Class cls /*nonpointer=maybe*/);
    void initInstanceIsa(Class cls, bool hasCxxDtor);
}

2). NSObject

@interface NSObject <NSObject> {

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop

}

3). isa_t

union每次只使用其中一个成员,所以可以将isa_t转成Class类型,bits & ISA_MASK

#define ISA_MASK        0x0000000ffffffff8ULL

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }
    uintptr_t bits;

private:

    // Accessing the class requires custom ptrauth operations, so
    // force clients to go through setClass/getClass by making this
    // private.
    Class cls;
public:
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };

从定义可以看出

  • NSObjectobjc_object结构是一致的,编译器会把NSObject转化成objc_object
  • 同理,继承字NSObject的也会转化成objc_class
  • id本身就是指针,所以id不需要带*
  • 同理,Class也不需要

4). objc_class

struct objc_class : objc_object {

    // Class ISA;             //继承了一个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();
    }
}

5). cache_t

struct cache_t {

private:
    //显示原子性,保证增删改查线程安全
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask; //8
    union {
        struct {
            explicit_atomic<mask_t>    _maybeMask; //4
#if __LP64__
            uint16_t                   _flags;     //2
#endif
            uint16_t                   _occupied;  //2
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache;
    };
}

image.png

6). class_data_bits_t

// data pointer
#define FAST_DATA_MASK          0x00007ffffffffff8UL

struct class_data_bits_t {

    friend objc_class;

    // Values are the FAST_ flags above.
    uintptr_t bits;

public:
    class_rw_t* data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
    
    // Get the class's ro data, even in the presence of concurrent realization.
    // fixme this isn't really safe without a compiler barrier at least
    // and probably a memory barrier when realizeClass changes the data field
    const class_ro_t *safe_ro() const {
        class_rw_t *maybe_rw = data();
        if (maybe_rw->flags & RW_REALIZED) {
            // maybe_rw is rw
            return maybe_rw->ro();
        } else {
            // maybe_rw is actually ro
            return (class_ro_t *)maybe_rw;
        }
    }
}

7). class_rw_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

    explicit_atomic<uintptr_t> ro_or_rw_ext;
    Class firstSubclass;
    Class nextSiblingClass;

public:
    class_rw_ext_t *ext() const {
        return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
    }

    class_rw_ext_t *extAllocIfNeeded() {
        auto v = get_ro_or_rwe();
        if (fastpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext);
        } else {
            return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));
        }
    }
    
    const class_ro_t *ro() const {
        auto v = get_ro_or_rwe();
        if (slowpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
        }
        return v.get<const class_ro_t *>(&ro_or_rw_ext);
    }
    
    void set_ro(const class_ro_t *ro) {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro = ro;
        } else {
            set_ro_or_rwe(ro);
        }
    }

    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 *>(&ro_or_rw_ext)->methods;
        } else {
            return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->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 *>(&ro_or_rw_ext)->properties;
        } else {
            return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->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 *>(&ro_or_rw_ext)->protocols;
        } else {
            return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
        }
    }
}

8). 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;

    // With ptrauth, this is signed if it points to a small list, but
    // may be unsigned if it points to a big list.
    void *baseMethodList;

    protocol_list_t * baseProtocols;

    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;

    property_list_t *baseProperties;
}