OC 底层原理(6)-类原理(指针内存偏移,类的结构)(随记)

675 阅读5分钟

一、指针内存偏移

1)指针地址

a)普通指针(值拷贝)

看到两个变量赋值一个常量,答应值和地址时,值一样地址不一样,对于此称之为值拷贝;&a 的意思是,指向这个变量的地址。

b)对象(指针拷贝)

此时对象的内存空间完全不一样,是因为在alloc的过程了两个对象申请的是不同的内存空间,&p1 的意思是,指向这个对象指针的地址。

c)数组指针

上图想表达的就是,打印的第一个和第一个地址是一样,因为数组的首地址就是数组的第一个元素的地址,跟打印对象内存空间时,内存的首地址就是对象的地址一个道理。

2)指针偏移

这里的 d, d+1, d+2,就是对指针地址偏移的实际运用,打印的结果就代表数组三个元素的地址,每个地址相差 4 ,是因为这个数组为 int 型。

通过对比,通过下标取值和通过指针内存偏移再取值得到的结果都相同。

通过打印数组首地址的到这个数组的内存空间,再读取值所在的内存段,也可以打印出来,为什么内存里面显示的顺序为,2,1,3 ?因为iOS为小端模式。

希望通过以上分析,对理解指针内存偏移有所帮助。

二、类的结构 先打印看下类的内存空间

如图我已经将几个前面提到的地址打印出来并知道代表什么了,那剩下的几个是代表什么?一起往下分析

对要分析的文件clang一下,我这里是 main.m ,clang 一下就是 main.cpp,然后并打开它。

还是直接划到最底部,这次从main函数开始分析,这张图片的代码内存是跟上面那张是对应的,OC 编译为 C++.

这部分既然是对类的结构分析,那么就在main.cpp中查找一下 Class 的定义看是什么样,见下图

原来Class的真正类型为 objc_class;还有直接在项目里不用clang,直接选中 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();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }

    void setInfo(uint32_t set) {
        assert(isFuture()  ||  isRealized());
        data()->setFlags(set);
    }

    void clearInfo(uint32_t clear) {
        assert(isFuture()  ||  isRealized());
        data()->clearFlags(clear);
    }

    // set and clear must not overlap
    void changeInfo(uint32_t set, uint32_t clear) {
        assert(isFuture()  ||  isRealized());
        assert((set & clear) == 0);
        data()->changeFlags(set, clear);
    }

    bool hasCustomRR() {
        return ! bits.hasDefaultRR();
    }
    void setHasDefaultRR() {
        assert(isInitializing());
        bits.setHasDefaultRR();
    }
    void setHasCustomRR(bool inherited = false);
    void printCustomRR(bool inherited);

    bool hasCustomAWZ() {
        return ! bits.hasDefaultAWZ();
    }
    void setHasDefaultAWZ() {
        assert(isInitializing());
        bits.setHasDefaultAWZ();
    }
    void setHasCustomAWZ(bool inherited = false);
    void printCustomAWZ(bool inherited);

    bool instancesRequireRawIsa() {
        return bits.instancesRequireRawIsa();
    }
    void setInstancesRequireRawIsa(bool inherited = false);
    void printInstancesRequireRawIsa(bool inherited);

    bool canAllocNonpointer() {
        assert(!isFuture());
        return !instancesRequireRawIsa();
    }
    bool canAllocFast() {
        assert(!isFuture());
        return bits.canAllocFast();
    }


    bool hasCxxCtor() {
        // addSubclass() propagates this flag from the superclass.
        assert(isRealized());
        return bits.hasCxxCtor();
    }
    void setHasCxxCtor() { 
        bits.setHasCxxCtor();
    }

    bool hasCxxDtor() {
        // addSubclass() propagates this flag from the superclass.
        assert(isRealized());
        return bits.hasCxxDtor();
    }
    void setHasCxxDtor() { 
        bits.setHasCxxDtor();
    }


    bool isSwiftStable() {
        return bits.isSwiftStable();
    }

    bool isSwiftLegacy() {
        return bits.isSwiftLegacy();
    }

    bool isAnySwift() {
        return bits.isAnySwift();
    }


    // Return YES if the class's ivars are managed by ARC, 
    // or the class is MRC but has ARC-style weak ivars.
    bool hasAutomaticIvars() {
        return data()->ro->flags & (RO_IS_ARC | RO_HAS_WEAK_WITHOUT_ARC);
    }

    // Return YES if the class's ivars are managed by ARC.
    bool isARC() {
        return data()->ro->flags & RO_IS_ARC;
    }


#if SUPPORT_NONPOINTER_ISA
    // Tracked in non-pointer isas; not tracked otherwise
#else
    bool instancesHaveAssociatedObjects() {
        // this may be an unrealized future class in the CF-bridged case
        assert(isFuture()  ||  isRealized());
        return data()->flags & RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS;
    }

    void setInstancesHaveAssociatedObjects() {
        // this may be an unrealized future class in the CF-bridged case
        assert(isFuture()  ||  isRealized());
        setInfo(RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS);
    }
#endif

    bool shouldGrowCache() {
        return true;
    }

    void setShouldGrowCache(bool) {
        // fixme good or bad for memory use?
    }

    bool isInitializing() {
        return getMeta()->data()->flags & RW_INITIALIZING;
    }

    void setInitializing() {
        assert(!isMetaClass());
        ISA()->setInfo(RW_INITIALIZING);
    }

    bool isInitialized() {
        return getMeta()->data()->flags & RW_INITIALIZED;
    }

    void setInitialized();

    bool isLoadable() {
        assert(isRealized());
        return true;  // any class registered for +load is definitely loadable
    }

    IMP getLoadMethod();

    // Locking: To prevent concurrent realization, hold runtimeLock.
    bool isRealized() {
        return data()->flags & RW_REALIZED;
    }

    // Returns true if this is an unrealized future class.
    // Locking: To prevent concurrent realization, hold runtimeLock.
    bool isFuture() { 
        return data()->flags & RW_FUTURE;
    }

    bool isMetaClass() {
        assert(this);
        assert(isRealized());
        return data()->ro->flags & RO_META;
    }

    // NOT identical to this->ISA when this is a metaclass
    Class getMeta() {
        if (isMetaClass()) return (Class)this;
        else return this->ISA();
    }

    bool isRootClass() {
        return superclass == nil;
    }
    bool isRootMetaclass() {
        return ISA() == (Class)this;
    }

    const char *mangledName() { 
        // fixme can't assert locks here
        assert(this);

        if (isRealized()  ||  isFuture()) {
            return data()->ro->name;
        } else {
            return ((const class_ro_t *)data())->name;
        }
    }
    
    const char *demangledName(bool realize = false);
    const char *nameForLogging();

    // May be unaligned depending on class's ivars.
    uint32_t unalignedInstanceStart() {
        assert(isRealized());
        return data()->ro->instanceStart;
    }

    // Class's instance start rounded up to a pointer-size boundary.
    // This is used for ARC layout bitmaps.
    uint32_t alignedInstanceStart() {
        return word_align(unalignedInstanceStart());
    }

    // May be unaligned depending on class's ivars.
    uint32_t unalignedInstanceSize() {
        assert(isRealized());
        return data()->ro->instanceSize;
    }

    // Class's ivar size rounded up to a pointer-size boundary.
    uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }

    size_t instanceSize(size_t extraBytes) {
        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16 bytes.
        if (size < 16) size = 16;
        return size;
    }

    void setInstanceSize(uint32_t newSize) {
        assert(isRealized());
        if (newSize != data()->ro->instanceSize) {
            assert(data()->flags & RW_COPIED_RO);
            *const_cast<uint32_t *>(&data()->ro->instanceSize) = newSize;
        }
        bits.setFastInstanceSize(newSize);
    }

    void chooseClassArrayIndex();

    void setClassArrayIndex(unsigned Idx) {
        bits.setClassArrayIndex(Idx);
    }

    unsigned classArrayIndex() {
        return bits.classArrayIndex();
    }

};

嗯,好多。还是从头看,原来 objc_class 是继承于 objc_object,万物皆对象,其实 objc_class 也是一种对象,从这句代码就可以说明。

// Class ISA;:在这个结构体开始出有一个隐藏的isa,那为什么是隐藏的呢?

因为这个isa 来自继承,也就是来自 objc_object(下图)

这其实就标明了,最后的大 boss 就是 objc_object ,那跟之前有提到的跟元类和根根元类的NSObject 是什么关系呢?然后又看一下 NSObject 的源码并贴出来

好吧,objc_object 和 NSObject 的内部结构都一样的,都是 Class isa

还记得这部分开始解析时,打印了类的内存结构,刚才还有几个地址并不知道代表的是什么,根据下图,类的结构源码便可知

0x100001468 :首地址指向的是 isa

0x001d800100001441:isa

0x0000000100b36140:superclass

0x100001478:此首地址指向的是 cache

0x00000001003d9260:cache

0x0000000000000000: bits

但是我们看都第四段还有感觉不太对,全是0,其实它存的东西是很对,所以后面再进行分析。

根据以上,对class的类型和结构就有个大概的了解了。

补充:

1)objc_class 和 NSObject 的关系?

NSObject 是 objc_class 的类型,因为 NSObject 也是一个 class。

2)objc_objce 和 NSObject 的关系?

任何的上层代码是OC,OC底层封装的就是C,objc_objce 是NSObject的底层实现,也就是本质。