美文网首页
四、类原理之isa

四、类原理之isa

作者: Mlqq | 来源:发表于2021-03-25 18:36 被阅读0次

    1、Class底层结构分析

    联合体和ISA一篇文章中介绍了对象的Class方法其实是获取对象的isa.shiftbits,那么Class对应的数据结构是什么呢。

    image.png
    从定义中可以看到其实Class就是一个指针,指向一个objc_class结构体。objc_class的定义如下:
    struct objc_class : objc_object {
      objc_class(const objc_class&) = delete;
      objc_class(objc_class&&) = delete;
      void operator=(const objc_class&) = delete;
      void operator=(objc_class&&) = delete;
        // 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 getSuperclass() const {
    #if __has_feature(ptrauth_calls)
    #   if ISA_SIGNING_AUTH_MODE == ISA_SIGNING_AUTH
            if (superclass == Nil)
                return Nil;
    
    #if SUPERCLASS_SIGNING_TREAT_UNSIGNED_AS_NIL
            void *stripped = ptrauth_strip((void *)superclass, ISA_SIGNING_KEY);
            if ((void *)superclass == stripped) {
                void *resigned = ptrauth_sign_unauthenticated(stripped, ISA_SIGNING_KEY, ptrauth_blend_discriminator(&superclass, ISA_SIGNING_DISCRIMINATOR_CLASS_SUPERCLASS));
                if ((void *)superclass != resigned)
                    return Nil;
            }
    #endif
                
            void *result = ptrauth_auth_data((void *)superclass, ISA_SIGNING_KEY, ptrauth_blend_discriminator(&superclass, ISA_SIGNING_DISCRIMINATOR_CLASS_SUPERCLASS));
            return (Class)result;
    
    #   else
            return (Class)ptrauth_strip((void *)superclass, ISA_SIGNING_KEY);
    #   endif
    #else
            return superclass;
    #endif
        }
    
        void setSuperclass(Class newSuperclass) {
    #if ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_ALL
            superclass = (Class)ptrauth_sign_unauthenticated((void *)newSuperclass, ISA_SIGNING_KEY, ptrauth_blend_discriminator(&superclass, ISA_SIGNING_DISCRIMINATOR_CLASS_SUPERCLASS));
    #else
            superclass = newSuperclass;
    #endif
        }
    
        class_rw_t *data() const {
            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);
        }
    
    #if FAST_HAS_DEFAULT_RR
        bool hasCustomRR() const {
            return !bits.getBit(FAST_HAS_DEFAULT_RR);
        }
        void setHasDefaultRR() {
            bits.setBits(FAST_HAS_DEFAULT_RR);
        }
        void setHasCustomRR() {
            bits.clearBits(FAST_HAS_DEFAULT_RR);
        }
    #else
        bool hasCustomRR() const {
            return !(bits.data()->flags & RW_HAS_DEFAULT_RR);
        }
        void setHasDefaultRR() {
            bits.data()->setFlags(RW_HAS_DEFAULT_RR);
        }
        void setHasCustomRR() {
            bits.data()->clearFlags(RW_HAS_DEFAULT_RR);
        }
    #endif
    
    #if FAST_CACHE_HAS_DEFAULT_AWZ
        bool hasCustomAWZ() const {
            return !cache.getBit(FAST_CACHE_HAS_DEFAULT_AWZ);
        }
        void setHasDefaultAWZ() {
            cache.setBit(FAST_CACHE_HAS_DEFAULT_AWZ);
        }
        void setHasCustomAWZ() {
            cache.clearBit(FAST_CACHE_HAS_DEFAULT_AWZ);
        }
    #else
        bool hasCustomAWZ() const {
            return !(bits.data()->flags & RW_HAS_DEFAULT_AWZ);
        }
        void setHasDefaultAWZ() {
            bits.data()->setFlags(RW_HAS_DEFAULT_AWZ);
        }
        void setHasCustomAWZ() {
            bits.data()->clearFlags(RW_HAS_DEFAULT_AWZ);
        }
    #endif
    
    #if FAST_CACHE_HAS_DEFAULT_CORE
        bool hasCustomCore() const {
            return !cache.getBit(FAST_CACHE_HAS_DEFAULT_CORE);
        }
        void setHasDefaultCore() {
            return cache.setBit(FAST_CACHE_HAS_DEFAULT_CORE);
        }
        void setHasCustomCore() {
            return cache.clearBit(FAST_CACHE_HAS_DEFAULT_CORE);
        }
    #else
        bool hasCustomCore() const {
            return !(bits.data()->flags & RW_HAS_DEFAULT_CORE);
        }
        void setHasDefaultCore() {
            bits.data()->setFlags(RW_HAS_DEFAULT_CORE);
        }
        void setHasCustomCore() {
            bits.data()->clearFlags(RW_HAS_DEFAULT_CORE);
        }
    #endif
    
    #if FAST_CACHE_HAS_CXX_CTOR
        bool hasCxxCtor() {
            ASSERT(isRealized());
            return cache.getBit(FAST_CACHE_HAS_CXX_CTOR);
        }
        void setHasCxxCtor() {
            cache.setBit(FAST_CACHE_HAS_CXX_CTOR);
        }
    #else
        bool hasCxxCtor() {
            ASSERT(isRealized());
            return bits.data()->flags & RW_HAS_CXX_CTOR;
        }
        void setHasCxxCtor() {
            bits.data()->setFlags(RW_HAS_CXX_CTOR);
        }
    #endif
    
    #if FAST_CACHE_HAS_CXX_DTOR
        bool hasCxxDtor() {
            ASSERT(isRealized());
            return cache.getBit(FAST_CACHE_HAS_CXX_DTOR);
        }
        void setHasCxxDtor() {
            cache.setBit(FAST_CACHE_HAS_CXX_DTOR);
        }
    #else
        bool hasCxxDtor() {
            ASSERT(isRealized());
            return bits.data()->flags & RW_HAS_CXX_DTOR;
        }
        void setHasCxxDtor() {
            bits.data()->setFlags(RW_HAS_CXX_DTOR);
        }
    #endif
    
    #if FAST_CACHE_REQUIRES_RAW_ISA
        bool instancesRequireRawIsa() {
            return cache.getBit(FAST_CACHE_REQUIRES_RAW_ISA);
        }
        void setInstancesRequireRawIsa() {
            cache.setBit(FAST_CACHE_REQUIRES_RAW_ISA);
        }
    #elif SUPPORT_NONPOINTER_ISA
        bool instancesRequireRawIsa() {
            return bits.data()->flags & RW_REQUIRES_RAW_ISA;
        }
        void setInstancesRequireRawIsa() {
            bits.data()->setFlags(RW_REQUIRES_RAW_ISA);
        }
    #else
        bool instancesRequireRawIsa() {
            return true;
        }
        void setInstancesRequireRawIsa() {
            // nothing
        }
    #endif
        void setInstancesRequireRawIsaRecursively(bool inherited = false);
        void printInstancesRequireRawIsa(bool inherited);
    
    #if CONFIG_USE_PREOPT_CACHES
        bool allowsPreoptCaches() const {
            return !(bits.data()->flags & RW_NOPREOPT_CACHE);
        }
        bool allowsPreoptInlinedSels() const {
            return !(bits.data()->flags & RW_NOPREOPT_SELS);
        }
        void setDisallowPreoptCaches() {
            bits.data()->setFlags(RW_NOPREOPT_CACHE | RW_NOPREOPT_SELS);
        }
        void setDisallowPreoptInlinedSels() {
            bits.data()->setFlags(RW_NOPREOPT_SELS);
        }
        void setDisallowPreoptCachesRecursively(const char *why);
        void setDisallowPreoptInlinedSelsRecursively(const char *why);
    #else
        bool allowsPreoptCaches() const { return false; }
        bool allowsPreoptInlinedSels() const { return false; }
        void setDisallowPreoptCaches() { }
        void setDisallowPreoptInlinedSels() { }
        void setDisallowPreoptCachesRecursively(const char *why) { }
        void setDisallowPreoptInlinedSelsRecursively(const char *why) { }
    #endif
    
        bool canAllocNonpointer() {
            ASSERT(!isFuture());
            return !instancesRequireRawIsa();
        }
    
        bool isSwiftStable() {
            return bits.isSwiftStable();
        }
    
        bool isSwiftLegacy() {
            return bits.isSwiftLegacy();
        }
    
        bool isAnySwift() {
            return bits.isAnySwift();
        }
    
        bool isSwiftStable_ButAllowLegacyForNow() {
            return bits.isSwiftStable_ButAllowLegacyForNow();
        }
    
        uint32_t swiftClassFlags() {
            return *(uint32_t *)(&bits + 1);
        }
      
        bool usesSwiftRefcounting() {
            if (!isSwiftStable()) return false;
            return bool(swiftClassFlags() & 2); //ClassFlags::UsesSwiftRefcounting
        }
    
        bool canCallSwiftRR() {
            // !hasCustomCore() is being used as a proxy for isInitialized(). All
            // classes with Swift refcounting are !hasCustomCore() (unless there are
            // category or swizzling shenanigans), but that bit is not set until a
            // class is initialized. Checking isInitialized requires an extra
            // indirection that we want to avoid on RR fast paths.
            //
            // In the unlikely event that someone causes a class with Swift
            // refcounting to be hasCustomCore(), we'll fall back to sending -retain
            // or -release, which is still correct.
            return !hasCustomCore() && usesSwiftRefcounting();
        }
    
        bool isStubClass() const {
            uintptr_t isa = (uintptr_t)isaBits();
            return 1 <= isa && isa < 16;
        }
    
        // Swift stable ABI built for old deployment targets looks weird.
        // The is-legacy bit is set for compatibility with old libobjc.
        // We are on a "new" deployment target so we need to rewrite that bit.
        // These stable-with-legacy-bit classes are distinguished from real
        // legacy classes using another bit in the Swift data
        // (ClassFlags::IsSwiftPreStableABI)
    
        bool isUnfixedBackwardDeployingStableSwift() {
            // Only classes marked as Swift legacy need apply.
            if (!bits.isSwiftLegacy()) return false;
    
            // Check the true legacy vs stable distinguisher.
            // The low bit of Swift's ClassFlags is SET for true legacy
            // and UNSET for stable pretending to be legacy.
            bool isActuallySwiftLegacy = bool(swiftClassFlags() & 1);
            return !isActuallySwiftLegacy;
        }
    
        void fixupBackwardDeployingStableSwift() {
            if (isUnfixedBackwardDeployingStableSwift()) {
                // Class really is stable Swift, pretending to be pre-stable.
                // Fix its lie.
                bits.setIsSwiftStable();
            }
        }
    
        _objc_swiftMetadataInitializer swiftMetadataInitializer() {
            return bits.swiftMetadataInitializer();
        }
    
        // 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;
        }
    
    
        bool forbidsAssociatedObjects() {
            return (data()->flags & RW_FORBIDS_ASSOCIATED_OBJECTS);
        }
    
    #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() const {
            return !isStubClass() && (data()->flags & RW_REALIZED);
        }
    
        // Returns true if this is an unrealized future class.
        // Locking: To prevent concurrent realization, hold runtimeLock.
        bool isFuture() const {
            if (isStubClass())
                return false;
            return data()->flags & RW_FUTURE;
        }
    
        bool isMetaClass() const {
            ASSERT_THIS_NOT_NULL;
            ASSERT(isRealized());
    #if FAST_CACHE_META
            return cache.getBit(FAST_CACHE_META);
    #else
            return data()->flags & RW_META;
    #endif
        }
    
        // Like isMetaClass, but also valid on un-realized classes
        bool isMetaClassMaybeUnrealized() {
            static_assert(offsetof(class_rw_t, flags) == offsetof(class_ro_t, flags), "flags alias");
            static_assert(RO_META == RW_META, "flags alias");
            if (isStubClass())
                return false;
            return data()->flags & RW_META;
        }
    
        // NOT identical to this->ISA when this is a metaclass
        Class getMeta() {
            if (isMetaClassMaybeUnrealized()) return (Class)this;
            else return this->ISA();
        }
    
        bool isRootClass() {
            return getSuperclass() == nil;
        }
        bool isRootMetaclass() {
            return ISA() == (Class)this;
        }
      
        // If this class does not have a name already, we can ask Swift to construct one for us.
        const char *installMangledNameForLazilyNamedClass();
    
        // Get the class's mangled name, or NULL if the class has a lazy
        // name that hasn't been created yet.
        const char *nonlazyMangledName() const {
            return bits.safe_ro()->getName();
        }
    
        const char *mangledName() { 
            // fixme can't assert locks here
            ASSERT_THIS_NOT_NULL;
    
            const char *result = nonlazyMangledName();
    
            if (!result) {
                // This class lazily instantiates its name. Emplace and
                // return it.
                result = installMangledNameForLazilyNamedClass();
            }
    
            return result;
        }
        
        const char *demangledName(bool needsLock);
        const char *nameForLogging();
    
        // May be unaligned depending on class's ivars.
        uint32_t unalignedInstanceStart() const {
            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() const {
            return word_align(unalignedInstanceStart());
        }
    
        // May be unaligned depending on class's ivars.
        uint32_t unalignedInstanceSize() const {
            ASSERT(isRealized());
            return data()->ro()->instanceSize;
        }
    
        // Class's ivar size rounded up to a pointer-size boundary.
        uint32_t alignedInstanceSize() const {
            return word_align(unalignedInstanceSize());
        }
    
        inline size_t instanceSize(size_t extraBytes) const {
            if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
                return cache.fastInstanceSize(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());
            ASSERT(data()->flags & RW_REALIZING);
            auto ro = data()->ro();
            if (newSize != ro->instanceSize) {
                ASSERT(data()->flags & RW_COPIED_RO);
                *const_cast<uint32_t *>(&ro->instanceSize) = newSize;
            }
            cache.setFastInstanceSize(newSize);
        }
    
        void chooseClassArrayIndex();
    
        void setClassArrayIndex(unsigned Idx) {
            bits.setClassArrayIndex(Idx);
        }
    
        unsigned classArrayIndex() {
            return bits.classArrayIndex();
        }
    }
    

    这是objc4-818.2版本的定义,其他版本有所不同,但主体结构是一样的。

    objc_class的定义struct objc_class : objc_object可以看到objc_class是继承objc_object的,在看看objc_object是的结构:

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

    可以看到objc_object只包含了一个类型为Classisa,所以objc_class结构体里面也有一个类型为Classisa。这里有点类似链表的结构,但是和链表又有一点不一样,链表中指向节点的类型都是相同的,但是Class的节点不是相同的,我们可以把它理解的链式结构。

    从注释/// Represents an instance of a class.可以知道objc_object是一个class的实例,也就是一个对象。由于objc_class是继承objc_object的,所以objc_class也是一个对象,我们称之为类对象

    这里也简单提一点我们经常用的id

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

    他就是一个指针,指向objc_object结构体的指针。从这也可以得知,OC中所有实例对象在底层结构都为objc_object

    从以上分析我们可以得出以下结论:

    • Class其实也是一个对象,我们称之为类对象。
    • 不管是类对象还是实例对象都有一个变量isa

    2、isa指向

    2.1 实例对象

    从上面分析得知实例对象里面只有一个isa指针,我们可以一步步来探索:
    1、 先利用x/4gx打印一下对象的内存结构:

    image.png
    通过对objc_object的分析,我们知道objc_object里面就只有一个isa,所以内存结构的一个8字节就是isa指向的内存,也就是0x011d80010000879d
    2、在对象原理二中已经介绍过可以同过ISA_MASK这个宏定义来看isa具体指向那个类:
    image.png
    可以看到isa指向的是类MlqqObject

    3、上一步中我么使用po命令打印了0x011d80010000879d & 0x00007ffffffffff8ULL的描述信息,我们可以换一个命令p/x,16进制输出一下其内存地址:

    image.png
    4、既然在第2步中,我们已经得到objisa指向了MlqqObject,那么我们再利用p/x打印一下类MlqqObject的地址:
    image.png
    我们可以看到MlqqObjec.class的地址跟我们在第3步中得到的地址一样。
    • 结论实例对象的isa是指向类的。

    2.2 类对象

    我们还可以再接着对MlqqObjec.class地址进行分析:
    1、还是利用x/4gx打印其内存结构:

    image.png
    2、 同理第18字节也为其isa,我们po输出其isa
    image.png
    同样为MlqqObject。

    3、我们再打印一下内存地址:

    image.png
    可见图中的两个地址并不相同,也就是说在上一步中得到的MlqqObject2.1中的第4步得到的MlqqObject并不是同一个类。

    4、我们可以继续打印上一步中的0x0000000100008770内存结构:

    image.png
    5、再po打印isa描述信息:
    image.png
    可以看到isa指向NSObject,那是我们理解的NSObject吗,把其内存地址打印出来,然后再把NSObject.class的内存地址打印出来,看看是不是相同:
    image.png

    可见两个地址并不相同,也就是说这两个东西不是同一个类。
    同时可以看到其isa还是指向0x00007fff88966fe0

    6、分析一下NSObject.class内存结构,并查看其isa

    image.png
    可见NSObject.classisa也是指向0x00007fff88966fe0

    总结以上分析我们可以得到一下关系图:


    image.png

    3、元类

    通过objc_class的定义我们知道,Class其实也是一个对象,对象是类的实例化,那么实例化Class对象的类,我们称之为元类元类的创建和管理都是系统在维护,不需要我们管理。

    image.png
    明确了元类的定义,我们就清楚了这两类就是元类了。

    总结:通过上面几步的分析,我们就可以理解那张讲到isa,不得不提的一张图:


    image.png

    相关文章

      网友评论

          本文标题:四、类原理之isa

          本文链接:https://www.haomeiwen.com/subject/orsgcltx.html