探索OC對象的本質(zhì)
我們知道平時編寫的Objective-C
代碼,底層都是C/C++
實現(xiàn)的,那么C/C++
又是怎么實現(xiàn)的呢?
- 下面看一段我們非常熟悉的
OC
代碼:
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end
@implementation Person
@end
- 通過
Clang
編譯成C/C++
:
clang -rewrite-objc main.m -o main.cpp
- 編譯成功后
extern "C" unsigned long OBJC_IVAR_$_Person$_name;
// Person 類對象
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS; //isa
// 屬性
NSString *_name;
};
// @property (nonatomic, copy) NSString *name;
/* @end */
// @implementation Person
// get 方法
static NSString * _I_Person_name(Person * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_Person$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
// set 方法
static void _I_Person_setName_(Person * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct Person, _name), (id)name, 0, 1); }
// @end
這里可以看到: 類對象
被轉(zhuǎn)化成了一個結(jié)構(gòu)體
- 在
OC
中進入NSObject
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
-
C++
代碼
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_name;
};
小結(jié): 兩個方面都證實NSObject
對象的本質(zhì)就是一個結(jié)構(gòu)體
isa流程分析
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
- 點擊
Class
進入
typedef struct objc_class *Class;
Class
是一個結(jié)構(gòu)體指針.所以isa
也就是一個指針
通過這篇文章得知,對象在alloc
過程中,開辟完空間需要關(guān)聯(lián)isa
isa
的創(chuàng)建和關(guān)聯(lián)流程是怎樣的呢,下面我們來以前探索
- 對象創(chuàng)建過程中,關(guān)聯(lián)
isa
的方法
obj->initInstanceIsa(cls, hasCxxDtor);
- 點擊進入
initInstanceIsa
初始化一個isa
inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
ASSERT(!cls->instancesRequireRawIsa());
ASSERT(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor);
}
- 進入
initIsa
初始化isa
函數(shù)
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
// 不存在,則創(chuàng)建isa
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;
// Class笛匙、Meta-Class對象的內(nèi)存地址信息賦值
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;
}
}
- 進去
isa_t
isa
的結(jié)構(gòu)是一個union
(聯(lián)合體)
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
};
- 進入
ISA_BITFIELD
(isa
位域)
// 真機
# 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)
// 模擬器
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
# else
# error unknown architecture for packed isa
# endif
isa
的大小為8
/字節(jié)即為64
位
isa初始化流程.png
isa結(jié)構(gòu)分析
通過上面分析得到,isa
是一個union
結(jié)構(gòu),用位域存儲信息
isa arm64 解釋
-
nonpointer
:表示是否對isa
指針開啟指針優(yōu)化0
:純isa指針,1
:不止是類對象地址,isa
中包含了類信息差导、對象的引用計數(shù)等 -
has_assoc
:關(guān)聯(lián)對象標志位,0
沒有借跪,1
存在 -
has_cxx_dtor
:該對象是否有C++
或者Objc
的析構(gòu)器,如果有析構(gòu)函數(shù),則需要做析構(gòu)邏輯, 如果沒有,則可以更快的釋放對象 -
shiftcls
:存儲類指針的值. 開啟指針優(yōu)化的情況下祭埂,在arm64
架構(gòu)中有33
位用來存儲類指針 -
magic
:用于調(diào)試器判斷當前對象是真的對象還是沒有初始化的空間 -
weakly_referenced
:對象是否被指向或者曾經(jīng)指向一個ARC
的弱變量, 沒有弱引用的對象可以更快釋放 -
deallocating
:標志對象是否正在釋放內(nèi)存 -
has_sidetable_rc
:當對象引用技術(shù)大于10
時, 則需要借用該變量存儲進位 -
extra_rc
:當表示該對象的引用計數(shù)值, 實際上是引用計數(shù)值減1
, 例如,如果對象的引用計數(shù)為10
卡儒,那么extra_rc
為9
,如果引用計數(shù)大于10
, 則需要使用到下面的has_sidetable_rc
arm64&x86_64.png
通過上圖我們可以得知shiftcls
存儲著類的地址信息
-
下面我們用
x86_64
驗證,Class
信息存儲在3~46
位
image.png 與
ISA_MASK &
運算
驗證4.png
- 查看源碼可知道
#if SUPPORT_NONPOINTER_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
// 與ISA_MASK 與運算
return (Class)(isa.bits & ISA_MASK);
#endif
}
對象關(guān)系分析
關(guān)系圖
總結(jié): 類和元類是一個閉環(huán), 實例指向類, 類指向元類, 元類指向跟元類, 跟元類指向自身, 根元類的父類是NSObject