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];
可以对objc1
和objc2
对象分析:
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;
};
根据LGPerson
是NSObject
子类,对象的内存地址绝对是同一块,这里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
对象编译后变成结构体后是个什么情况,即先是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
,因此我们可以知道类的内存结构和结构体类似,我们可以有objc1
和objc2
两个对象的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
调试:
isa
内容即0x011d800100008415
,用这个和ISA_MASK
做与运算可以得到:isa&ISA_MASK 可以证明
isa
可以通过与上ISA_MASK
转化为Class
。lldb打印地址 这里既然
po
出来可以得到Person
类,那就是一定存在地址的,x
出来得到地址0x100008410
,这个就应该是Person
的地址
类的地址
这样可以说明isa
和ISA_MASK
直接相与得到的就是对象的类即类对象存放在内存的地址。还不止这样,结合isa.bits
的分析shiftcls
表示存储类指针
,OMG!岂不是isa
和ISA_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
指向的就是自己的地址。这个就对应上网络中搜索到的那张isa
和superclass
指向图:
isa和superclass指向图
saler
=Instance of Subclass
对象Saler
=Subclass(class)
类Saler的类
=Subclass(meta)
元类Saler的类的类
=Root class(meta)
根元类
为验证这个图其实只要验证3点:
1、验证NSObject
的isa
是否指向的是根元类这个地址;
2、验证NSObject
这个类的superclass
为nil
;
3、验证NSObject
的isa
指向根元类。
证明第1点:
证明第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
;
NSObject
的isa
指向的是根元类,superclass
指向nil。
网友评论