4、isa与对象、类的关系

2020-11-25  本文已影响0人  白马啸红中
tips:iOS更新至14,Xcode更新至12.1,MacOS更新至11.0.1,
突发原objc781代码报错,原来更新为objc781.1还是会报错,缺少
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation
找到路径下,对比之前的MacOS文件路径缺少Foundation的mach-o文件,是系统的问题,这里待后续更新解决。 
这里暂时建议不升级到big sur。

1、对象分析

新建如下的类:

@interface LGPerson : NSObject
@property (nonatomic,strong)   NSString *name;
@end
@implementation LGPerson
@end

然后创建两个对象:

NSObject *objc1 = [[NSObject alloc] init];
LGPerson *objc2 = [[LGPerson alloc] init];

可以对objc1objc2对象分析:

LGPerson的父类是NSObject,可见NSObject对象中存在一个isa指向的是对应的Class,难道对象结构就只有一个isa吗,显然不可能,那要怎么继续分析,这里就要涉及到文件的编译——Clang编译'前端'(这个前端不是指代web前端),是苹果开发属于llvm编译器框架,支持C/C++/Objective-C
//编译命令
clang -rewrite-objc main.m -o main.cpp

由于这里创建对象代码是写在main文件中的,这里编译的就是main文件,得到main.cpp文件,搜索LGPerson:

#ifndef _REWRITER_typedef_LGPerson
#define _REWRITER_typedef_LGPerson
typedef struct objc_object LGPerson;
typedef struct {} _objc_exc_LGPerson;
#endif

struct LGPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
};

// @property (nonatomic,strong) NSString *name;

/* @end */

这里就是关于LGPerson的对象的定义,即会被编译成一个结构体struct,所以在OC中,对象的本质来说还是一个结构体,那么普通结构体与对象的结构体有什么区别吗?可能就在struct NSObject_IMPL NSObject_IVARS;这个结构体中了。这就是C语言中实现‘继承’的方式,将NSObject类中的属性全部在子类中保存一份在自己的类中,同步信息达到继承的目的。对比NSObject的编译后代码,

struct NSObject_IMPL {
    Class isa;
};

根据LGPersonNSObject子类,对象的内存地址绝对是同一块,这里struct NSObject_IMPL NSObject_IVARS;Class isa;应该是指代的同一块内存,所以是等同的。

为什么没有name属性的相关信息,这里猜测是因为后续也没有对name属性使用或者赋值。所以重新定义一个类:

@interface Person : NSObject
@property (nonatomic,assign)   int age;
@property (nonatomic,strong)   NSString *nickname;
@property (nonatomic,assign)   float height;
@property (nonatomic,strong)   NSString *name;
@end

//main方法中赋值
Person *person =  [Person alloc];
person.age = 10;
person.nickname = @"pp";
person.height = 180.0;
person.name = @"ppext";
        
NSLog(@"%lu",sizeof(person));

再对这个main.m进行clang编译,果然就出现了:

编译后的Person类 这样就基本确定了,person对象编译后变成结构体后是个什么情况,即先是isa指针,然后是属性依次排列。
但是这里存在一个问题就是typedef struct objc_object LGPerson这里LGPerson显然是个类,但是定义是一个objc_object名字为对象的结构体定义的,有点奇怪。在objc源码中全局搜索objc_object,可以搜到这个结构体定义,也可以发现其中确实包含isa,同时也发现另一个结构体:
typedef struct objc_class *Class;
typedef struct objc_object *id;
typedef struct classref *classref_t;

这段代码就可以确定oc中id类型就是一个对象结构体。这个objc_class居然是Class类,定义是什么样子的,因此也全局搜索到两段代码:

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
   ....
} OBJC2_UNAVAILABLE;

这段代码可以发现在OBJC2就不可用,现阶段代码是OBJC4,所以这是一段旧代码可以方便理解之前的代码逻辑。
第二段代码:

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() 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);

    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();
    }

    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.
        uint32_t swiftClassFlags = *(uint32_t *)(&bits + 1);
        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 {
        return data()->flags & RW_FUTURE;
    }

    bool isMetaClass() {
        ASSERT(this);
        ASSERT(isRealized());
#if FAST_CACHE_META
        return cache.getBit(FAST_CACHE_META);
#else
        return data()->flags & RW_META;
#endif
    }

这里objc_class继承至objc_object,说明Class也是一个对象,这样也验证了一个概念,在Objective-C中类也是对象,即类对象。而且类对象结构与对象的结构差不多,只是多了后面一点属性和方法。而且可以发现方法中多了MetaClass这个概念,即元类,这个元类的是什么意思待后续再研究。

2、isa分析

之前的分析可以知道,对象和类中都含有isa这个属性,这里要分析一下isa到底含有什么信息。
根据之前alloc分析、源码调试(3)这片文章的分析,isa的初始化是调用的initIsa方法,进入源码分析发现isa是一个isa_t的Union联合体。

isa = isa_t((uintptr_t)cls);

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

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

且这个联合体中除了cls主要的是一个bits,由字面意思可知这是一个纯``类型的成员,为什么不是类似cls这种占8字节的指针类型呢,表意不是更清晰吗?

这里确实存在这节省的原因,类似寄存器的标志位,信息的表示只需0或者1两种,对比bool型占用一个字节节省很多存储空间,而且Union的成员是互斥的,即存的cls则bits就没有,如果存bits的话cls就没有。
arm64环境下,bits的位定义:(这里至贴了arm64的,还有x86的没贴出来)

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
#   define ISA_BITFIELD                                                      \
      uintptr_t nonpointer        : 1;                                       \
      uintptr_t has_assoc         : 1;                                       \
      uintptr_t has_cxx_dtor      : 1;                                       \
      uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
      uintptr_t magic             : 6;                                       \
      uintptr_t weakly_referenced : 1;                                       \
      uintptr_t deallocating      : 1;                                       \
      uintptr_t has_sidetable_rc  : 1;                                       \
      uintptr_t extra_rc          : 19
#   define RC_ONE   (1ULL<<45)
#   define RC_HALF  (1ULL<<18)
isa64情况 可以知道bits是一个64位,存储了很多信息的成员。
nonpointer/indeed:表示是否对 isa 指针开启指针优化 0:纯isa指针,1:不止是类对象地址,isa 中包含了类信息、对象的引用计数等。
has_assoc:字面意思是否有关联对象,0没有,1存在
has_cxx_dtor:该对象是否有 C++ 或者 Objc 的析构器,如果有析构函数,则需要做
shiftcls:存储类指针的值。开启指针优化的情况下,在 arm64 架构中有 33 位用来存储类指针。
magic:用于调试器判断当前对象是真的对象还是没有初始化的空间。
weakly_referenced:标志对象是否被指向或者曾经指向一个 ARC 的弱变量,
没有弱引用的对象可以更快释放。
deallocating:标志对象是否正在释放内存
has_sidetable_rc:当对象引用技术大于 10 时,则需要借用该变量存储进位
extra_rc:当表示该对象的引用计数值,实际上是引用计数值减 1, 例如,如果对象的引用计数为 10,那么 extra_rc 为 9。如果引用计数大于 10, 则需要使用到下面的 has_sidetable_rc。
可以知道这些信息就是对象的一些默认信息,也是类的信息。
对标initIsa源码
inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    ASSERT(!isTaggedPointer()); 
    
    if (!nonpointer) {
        isa = isa_t((uintptr_t)cls);
    } else {
        ASSERT(!DisableNonpointerIsa);
        ASSERT(!cls->instancesRequireRawIsa());

        isa_t newisa(0);

#if SUPPORT_INDEXED_ISA
        ASSERT(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.shiftcls = (uintptr_t)cls >> 3;
#endif

        // This write must be performed in a single store in some cases
        // (for example when realizing a class because other threads
        // may simultaneously try to use the class).
        // fixme use atomics here to guarantee single-store and to
        // guarantee memory order w.r.t. the class index table
        // ...but not too atomic because we don't want to hurt instantiation
        isa = newisa;
    }
}

发现就是通过直接设置bits来实现isa的初始化,其中断点走的是ISA_MAGIC_VALUE分支,查阅定义define ISA_MAGIC_VALUE 0x001d800000000001UL,其第一位是1,结合之前的各个位定义,表示这个生成的isa并不是nonpointer类型,而是包含了各种信息的。后面两句代码还对has_cxx_dtor位和shiftcls进行了赋值。
由于对象和类在底层的实现是Struct,因此我们可以知道类的内存结构和结构体类似,我们可以有objc1objc2两个对象的isa是存在对象的内存中的,怎么知道内存地址是怎么布局,就涉及到结构体内存对齐原理,可以参考结构体内存对齐
这里分析完就可以知道,isa是包含了对象的信息,但是仅有这些表示的信息还不够,属性、方法一个都不知道,那继续分析,回到代码层面去寻找isa与类信息的真实关系。

3、isa与类

首先,经过clang编译后我们可以知道对象其实是objc_object结构体,那我们就继续去找这个结构体源代码,果不其然在objc4-787.1的源码中可以找到相关定义:

struct objc_object {
private:
    isa_t isa;

public:

    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();

    // 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);

    // changeIsa() should be used to change the isa of existing objects.
    // If this is a new object, use initIsa() for performance.
    Class changeIsa(Class newCls);

    bool hasNonpointerIsa();
    bool isTaggedPointer();
    bool isBasicTaggedPointer();
    bool isExtTaggedPointer();
    bool isClass();

    // object may have associated objects?
    bool hasAssociatedObjects();
    void setHasAssociatedObjects();

    // object may be weakly referenced?
    bool isWeaklyReferenced();
    void setWeaklyReferenced_nolock();

    // object may have -.cxx_destruct implementation?
    bool hasCxxDtor();

    // Optimized calls to retain/release methods
    id retain();
    void release();
    id autorelease();

    // Implementations of retain/release methods
    id rootRetain();
    bool rootRelease();
    id rootAutorelease();
    bool rootTryRetain();
    bool rootReleaseShouldDealloc();
    uintptr_t rootRetainCount();

    // Implementation of dealloc methods
    bool rootIsDeallocating();
    void clearDeallocating();
    void rootDealloc();

private:
    void initIsa(Class newCls, bool nonpointer, bool hasCxxDtor);

    // Slow paths for inline control
    id rootAutorelease2();
    uintptr_t overrelease_error();

#if SUPPORT_NONPOINTER_ISA
    // Unified retain count manipulation for nonpointer isa
    id rootRetain(bool tryRetain, bool handleOverflow);
    bool rootRelease(bool performDealloc, bool handleUnderflow);
    id rootRetain_overflow(bool tryRetain);
    uintptr_t rootRelease_underflow(bool performDealloc);

    void clearDeallocating_slow();

    // Side table retain count overflow for nonpointer isa
    void sidetable_lock();
    void sidetable_unlock();

    void sidetable_moveExtraRC_nolock(size_t extra_rc, bool isDeallocating, bool weaklyReferenced);
    bool sidetable_addExtraRC_nolock(size_t delta_rc);
    size_t sidetable_subExtraRC_nolock(size_t delta_rc);
    size_t sidetable_getExtraRC_nolock();
#endif

    // Side-table-only retain count
    bool sidetable_isDeallocating();
    void sidetable_clearDeallocating();

    bool sidetable_isWeaklyReferenced();
    void sidetable_setWeaklyReferenced_nolock();

    id sidetable_retain();
    id sidetable_retain_slow(SideTable& table);

    uintptr_t sidetable_release(bool performDealloc = true);
    uintptr_t sidetable_release_slow(SideTable& table, bool performDealloc = true);

    bool sidetable_tryRetain();

    uintptr_t sidetable_retainCount();
#if DEBUG
    bool sidetable_present();
#endif
};

可以发现和之前研究的一样,每个对象定义中必须包含一个isa,而且还发现了不管是Class ISA();或者是Class getIsa();方法返回的并不是isa_t这个原生isa类型而是Class这个类型观察Class ISA();方法源码:

inline Class 
objc_object::ISA() 
{
    ASSERT(!isTaggedPointer()); 
#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    return (Class)(isa.bits & ISA_MASK);
#endif
}

其实Class就是isa转化来,只是通过与上ISA_MASK这个值(x86:0x00007ffffffffff8ULL)。与上这个值有啥用呢?这个只有调试一下得知:

//main方法中
Person *person =  [Person alloc];
person.age = 10;
person.nickname = @"pp";
person.height = 180.0;
person.name = @"ppext";

进行lldb调试:

lldb调试 可以知道第一个8字节就是isa内容即0x011d800100008415,用这个和ISA_MASK做与运算可以得到:
isa&ISA_MASK 可以证明isa可以通过与上ISA_MASK转化为Class
lldb打印地址 这里既然po出来可以得到Person类,那就是一定存在地址的,x出来得到地址0x100008410,这个就应该是Person的地址 类的地址 这样可以说明isaISA_MASK直接相与得到的就是对象的类即类对象存放在内存的地址。还不止这样,结合isa.bits的分析shiftcls表示存储类指针,OMG!岂不是isaISA_MASK相与就是得到shiftcls这一段,这个相与位运算就是相当于左移动、右移动抹掉数据了。
这里就可以知道为什么isa包含了生成对象的类的信息,不仅表示的信息,还有shiftcls指向的类的信息。到此基本上就以确定了isa和类的关系,即通过isa可以找到类对象地址,然后找到类信息。

对象的isa指向类,那么类的isa指向谁呢?
新增一个子类

@interface Saler : Person
@property (nonatomic,strong)   NSString *brand;
@end

Saler *saler = [Saler alloc];
saler.age = 28;
saler.nickname = @"xx";
saler.height = 175.0;
saler.name = @"xxext";
saler.brand = @"apple";

lldb调试通过对象打印出类的地址:

对象的类的地址 计算并打印出类的isa指向的地址:
类的isa指向的地址 继续追踪isa指向:
继续追踪isa的指向 发现这样一个规律:
saler->Saler->Saler的类->Saler的类的类,最后一个就会发现其isa指向的就是自己的地址。这个就对应上网络中搜索到的那张isasuperclass指向图: isa和superclass指向图
saler=Instance of Subclass 对象
Saler=Subclass(class)
Saler的类=Subclass(meta) 元类
Saler的类的类=Root class(meta) 根元类

为验证这个图其实只要验证3点:
1、验证NSObjectisa是否指向的是根元类这个地址;
2、验证NSObject这个类的superclassnil;
3、验证NSObjectisa指向根元类。

证明第1点:

NSObject的isa指向地址
证明第2点:第一点图中NSObject发现其第二个8字节为空,按照objc_class结构体定义第一个8字节默认继承的isa第二个就是superclass刚好为nil
证明第3点:继续追踪isa的指向图中,最后一个即是根元类,其第二个8字节即是superclass,地址为:0x00007fff88a6fd88与第一点证明图的NSObject完全相同。
即三点都得到证明。

总结一下:

1、对象、类的底层实现都是结构体objc_object,类Class=objc_class,而且objc_class继承于objc_object,所以类也是一种对象,即类对象。
2、objc_object中含有isa属性,isa中包含对象的各种信息,而且通过位运算可以得到对象的类的地址。
3、类的isa指向的类被成为称为元类,而元类的isa指向的是根元类;
根元类的isa指向的是自己,superclass指向的是NSObject
NSObjectisa指向的是根元类,superclass指向nil。

上一篇 下一篇

猜你喜欢

热点阅读