前言
前面我們分析了對象&類&元類的層次結(jié)構(gòu),以及他們之間的關(guān)系。
顯然,類模版cls是我們整個OC面向?qū)ο笤O(shè)計里面的核心數(shù)據(jù)結(jié)構(gòu)。它保存了對象創(chuàng)建所需的信息(大小,成員,成員訪問路徑等),也保存了方法/屬性/協(xié)議/成員列表/緩存等共享內(nèi)容。
讓我們一期通過LLDB和源碼一步步探索認識類結(jié)構(gòu)的底層實現(xiàn)。
objc_class
objc4-818.2的源碼
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
上面是非objc2的,objc2之前的版本objc_class的定義,顯然不是我們研究的重點。
在objc-runtime-new.h中,找到了objc2的定義如下:
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
/**
此處省略大量方法定義以及針對不同平臺或者cpu架構(gòu)的處理,有興趣的同學(xué)可以查看源碼。
*/
}
新版的objc_clss定義是繼承自objc_object,繼承了isa指針成員。
當前結(jié)構(gòu)體有四個成員:isa、superClass、cahe、bits。
isa指向元類
superClass指向父類
cache是緩存,跟運行時相關(guān),我們后面單獨一個篇章來探索這個緩存機制。
那么bits可能就是存儲方法列表,屬性列表等信息的地方。
class_data_bits_t
struct class_data_bits_t {
//可以訪問objc_class的私有成員
friend objc_class;
// Values are the FAST_ flags above.
uintptr_t bits;
private:
bool getBit(uintptr_t bit) const
{
return bits & bit;
}
// Atomically set the bits in `set` and clear the bits in `clear`.
// set and clear must not overlap.
void setAndClearBits(uintptr_t set, uintptr_t clear)
{
ASSERT((set & clear) == 0);
uintptr_t newBits, oldBits = LoadExclusive(&bits);
do {
newBits = (oldBits | set) & ~clear;
} while (slowpath(!StoreReleaseExclusive(&bits, &oldBits, newBits)));
}
void setBits(uintptr_t set) {
__c11_atomic_fetch_or((_Atomic(uintptr_t) *)&bits, set, __ATOMIC_RELAXED);
}
void clearBits(uintptr_t clear) {
__c11_atomic_fetch_and((_Atomic(uintptr_t) *)&bits, ~clear, __ATOMIC_RELAXED);
}
public:
//獲取data,class_rw_t
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
void setData(class_rw_t *newData)
{
ASSERT(!data() || (newData->flags & (RW_REALIZING | RW_FUTURE)));
// Set during realization or construction only. No locking needed.
// Use a store-release fence because there may be concurrent
// readers of data and data's contents.
uintptr_t newBits = (bits & ~FAST_DATA_MASK) | (uintptr_t)newData;
atomic_thread_fence(memory_order_release);
bits = newBits;
}
// Get the class's ro data, even in the presence of concurrent realization.
// fixme this isn't really safe without a compiler barrier at least
// and probably a memory barrier when realizeClass changes the data field
const class_ro_t *safe_ro() const {
class_rw_t *maybe_rw = data();
if (maybe_rw->flags & RW_REALIZED) {
// maybe_rw is rw
return maybe_rw->ro();
} else {
// maybe_rw is actually ro
return (class_ro_t *)maybe_rw;
}
}
#if SUPPORT_INDEXED_ISA
void setClassArrayIndex(unsigned Idx) {
// 0 is unused as then we can rely on zero-initialisation from calloc.
ASSERT(Idx > 0);
data()->index = Idx;
}
#else
void setClassArrayIndex(__unused unsigned Idx) {
}
#endif
unsigned classArrayIndex() {
#if SUPPORT_INDEXED_ISA
return data()->index;
#else
return 0;
#endif
}
bool isAnySwift() {
return isSwiftStable() || isSwiftLegacy();
}
bool isSwiftStable() {
return getBit(FAST_IS_SWIFT_STABLE);
}
void setIsSwiftStable() {
setAndClearBits(FAST_IS_SWIFT_STABLE, FAST_IS_SWIFT_LEGACY);
}
bool isSwiftLegacy() {
return getBit(FAST_IS_SWIFT_LEGACY);
}
void setIsSwiftLegacy() {
setAndClearBits(FAST_IS_SWIFT_LEGACY, FAST_IS_SWIFT_STABLE);
}
// fixme remove this once the Swift runtime uses the stable bits
bool isSwiftStable_ButAllowLegacyForNow() {
return isAnySwift();
}
_objc_swiftMetadataInitializer swiftMetadataInitializer() {
// This function is called on un-realized classes without
// holding any locks.
// Beware of races with other realizers.
return safe_ro()->swiftMetadataInitializer();
}
};
class_data_bits_t結(jié)構(gòu)體的成員非常簡單,只有一個指針成員uintptr_t bits。
既然只有一個指針成員,為什么還要進行一次結(jié)構(gòu)體包裝?
經(jīng)過class_data_bits_t結(jié)構(gòu)體包裝,擴展了一系列的數(shù)據(jù)的訪問方法。
bits指針不僅存儲了class_rw_t結(jié)構(gòu)體的指針地址(可以通過data()函數(shù)獲得,也是我們類數(shù)據(jù)的主要存儲結(jié)構(gòu)),通過擴展方法還可以對bits的一些位進行設(shè)置,來存儲一些額外信息,比如:hasCustomRR、一些swift信息。這里看起來類似nonpointer_isa,對指針的空余位進行了利用,來存儲更多的信息,體現(xiàn)了蘋果對內(nèi)存的優(yōu)化。
class_data_bits_t更主要的作用是通過bits成員存儲了class_rw_t *指針。
class_rw_t
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint16_t witness;
#if SUPPORT_INDEXED_ISA
uint16_t index;
#endif
explicit_atomic<uintptr_t> ro_or_rw_ext;
Class firstSubclass;
Class nextSiblingClass;
private:
using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t, class_rw_ext_t, PTRAUTH_STR("class_ro_t"), PTRAUTH_STR("class_rw_ext_t")>;
const ro_or_rw_ext_t get_ro_or_rwe() const {
return ro_or_rw_ext_t{ro_or_rw_ext};
}
void set_ro_or_rwe(const class_ro_t *ro) {
ro_or_rw_ext_t{ro, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_relaxed);
}
void set_ro_or_rwe(class_rw_ext_t *rwe, const class_ro_t *ro) {
// the release barrier is so that the class_rw_ext_t::ro initialization
// is visible to lockless readers
rwe->ro = ro;
ro_or_rw_ext_t{rwe, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_release);
}
class_rw_ext_t *extAlloc(const class_ro_t *ro, bool deep = false);
public:
void setFlags(uint32_t set)
{
__c11_atomic_fetch_or((_Atomic(uint32_t) *)&flags, set, __ATOMIC_RELAXED);
}
void clearFlags(uint32_t clear)
{
__c11_atomic_fetch_and((_Atomic(uint32_t) *)&flags, ~clear, __ATOMIC_RELAXED);
}
// set and clear must not overlap
void changeFlags(uint32_t set, uint32_t clear)
{
ASSERT((set & clear) == 0);
uint32_t oldf, newf;
do {
oldf = flags;
newf = (oldf | set) & ~clear;
} while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
}
class_rw_ext_t *ext() const {
return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
}
class_rw_ext_t *extAllocIfNeeded() {
auto v = get_ro_or_rwe();
if (fastpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext);
} else {
return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));
}
}
class_rw_ext_t *deepCopy(const class_ro_t *ro) {
return extAlloc(ro, true);
}
const class_ro_t *ro() const {
auto v = get_ro_or_rwe();
if (slowpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
}
return v.get<const class_ro_t *>(&ro_or_rw_ext);
}
void set_ro(const class_ro_t *ro) {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro = ro;
} else {
set_ro_or_rwe(ro);
}
}
const method_array_t methods() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
} else {
return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
}
}
const property_array_t properties() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
} else {
return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
}
}
const protocol_array_t protocols() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
} else {
return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
}
}
};
class_rw_t結(jié)構(gòu)體有以下幾個成員:
- uint32_t flags;
標記位,通過掩碼運算,來存取一些bool信息,包括:
allowsPreoptCaches、allowsPreoptInlinedSels、instancesHaveAssociatedObjects、isInitializing、isInitialized、isRealized、isMetaClass、isMetaClassMaybeUnrealizedde - uint16_t witness;
如果一個類被實現(xiàn),witness記錄可以find的類的range
objc::dataSegmentsRanges.find((uintptr_t)cls, index)
witness = index - explicit_atomic<uintptr_t> ro_or_rw_ext;
進行原子性包裝的指針 - Class firstSubclass;
第一個子類 - Class nextSiblingClass;
兄弟類
cls->data()->nextSiblingClass = _firstRealizedClass;
subcls->data()->nextSiblingClass = supercls->data()->firstSubclass;
單獨看這四個成員,我們?nèi)匀粵]有找到方法,屬性,協(xié)議等信息的影子。但是我們發(fā)現(xiàn)class_rw_t有methods()、proprties()、protocols()三個方法。他們通過ro_or_rw_ext指針來獲取方法、屬性、協(xié)議等信息 。
ro_or_rw_ext
這個指針成員的實現(xiàn)也是非常的巧妙,它的存取都要經(jīng)過一次pointerUnion的轉(zhuǎn)換。ro_or_rw_ext可以是class_ro_t*,也可以是class_rw_ext_t,通過指針bit位的低一位進行區(qū)分。
private:
using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t, class_rw_ext_t, PTRAUTH_STR("class_ro_t"), PTRAUTH_STR("class_rw_ext_t")>;
const ro_or_rw_ext_t get_ro_or_rwe() const {
return ro_or_rw_ext_t{ro_or_rw_ext};
}
void set_ro_or_rwe(const class_ro_t *ro) {
ro_or_rw_ext_t{ro, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_relaxed);
}
void set_ro_or_rwe(class_rw_ext_t *rwe, const class_ro_t *ro) {
// the release barrier is so that the class_rw_ext_t::ro initialization
// is visible to lockless readers
rwe->ro = ro;
ro_or_rw_ext_t{rwe, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_release);
}
class_rw_ext_t *extAlloc(const class_ro_t *ro, bool deep = false);
****************************************************************
class PointerUnion {
uintptr_t _value;
static_assert(alignof(T1) >= 2, "alignment requirement");
static_assert(alignof(T2) >= 2, "alignment requirement");
struct IsPT1 {
static const uintptr_t Num = 0;
};
struct IsPT2 {
static const uintptr_t Num = 1;
};
template <typename T> struct UNION_DOESNT_CONTAIN_TYPE {};
uintptr_t getPointer() const {
return _value & ~1;
}
uintptr_t getTag() const {
return _value & 1;
}
public:
explicit PointerUnion(const std::atomic<uintptr_t> &raw)
: _value(raw.load(std::memory_order_relaxed))
{ }
PointerUnion(T1 *t, const void *address) {
_value = (uintptr_t)Auth1::sign(t, address);
}
PointerUnion(T2 *t, const void *address) {
_value = (uintptr_t)Auth2::sign(t, address) | 1;
}
void storeAt(std::atomic<uintptr_t> &raw, std::memory_order order) const {
raw.store(_value, order);
}
template <typename T>
bool is() const {
using Ty = typename PointerUnionTypeSelector<T1 *, T, IsPT1,
PointerUnionTypeSelector<T2 *, T, IsPT2,
UNION_DOESNT_CONTAIN_TYPE<T>>>::Return;
return getTag() == Ty::Num;
}
template <typename T> T get(const void *address) const {
ASSERT(is<T>() && "Invalid accessor called");
using AuthT = typename PointerUnionTypeSelector<T1 *, T, Auth1,
PointerUnionTypeSelector<T2 *, T, Auth2,
UNION_DOESNT_CONTAIN_TYPE<T>>>::Return;
return AuthT::auth((T)getPointer(), address);
}
template <typename T> T dyn_cast(const void *address) const {
if (is<T>())
return get<T>(address);
return T();
}
};
class_rw_ext_t
struct class_rw_ext_t {
DECLARE_AUTHED_PTR_TEMPLATE(class_ro_t)
class_ro_t_authed_ptr<const class_ro_t> ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
char *demangledName;
uint32_t version;
};
千呼萬喚始出來,終于看到了baseMethodList、baseProtocols、 ivars、weakIvarLayout、baseProperties這些內(nèi)容。那么class_ro_t又是什么呢?
class_ro_t
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
union {
const uint8_t * ivarLayout;
Class nonMetaclass;
};
explicit_atomic<const char *> name;
// With ptrauth, this is signed if it points to a small list, but
// may be unsigned if it points to a big list.
void *baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
// This field exists only when RO_HAS_SWIFT_INITIALIZER is set.
_objc_swiftMetadataInitializer __ptrauth_objc_method_list_imp _swiftMetadataInitializer_NEVER_USE[0];
_objc_swiftMetadataInitializer swiftMetadataInitializer() const {
if (flags & RO_HAS_SWIFT_INITIALIZER) {
return _swiftMetadataInitializer_NEVER_USE[0];
} else {
return nil;
}
}
const char *getName() const {
return name.load(std::memory_order_acquire);
}
static const uint16_t methodListPointerDiscriminator = 0xC310;
#if 0 // FIXME: enable this when we get a non-empty definition of __ptrauth_objc_method_list_pointer from ptrauth.h.
static_assert(std::is_same<
void * __ptrauth_objc_method_list_pointer *,
void * __ptrauth(ptrauth_key_method_list_pointer, 1, methodListPointerDiscriminator) *>::value,
"Method list pointer signing discriminator must match ptrauth.h");
#endif
method_list_t *baseMethods() const {
#if __has_feature(ptrauth_calls)
method_list_t *ptr = ptrauth_strip((method_list_t *)baseMethodList, ptrauth_key_method_list_pointer);
if (ptr == nullptr)
return nullptr;
// Don't auth if the class_ro and the method list are both in the shared cache.
// This is secure since they'll be read-only, and this allows the shared cache
// to cut down on the number of signed pointers it has.
bool roInSharedCache = objc::inSharedCache((uintptr_t)this);
bool listInSharedCache = objc::inSharedCache((uintptr_t)ptr);
if (roInSharedCache && listInSharedCache)
return ptr;
// Auth all other small lists.
if (ptr->isSmallList())
ptr = ptrauth_auth_data((method_list_t *)baseMethodList,
ptrauth_key_method_list_pointer,
ptrauth_blend_discriminator(&baseMethodList,
methodListPointerDiscriminator));
return ptr;
#else
return (method_list_t *)baseMethodList;
#endif
}
uintptr_t baseMethodListPtrauthData() const {
return ptrauth_blend_discriminator(&baseMethodList,
methodListPointerDiscriminator);
}
class_ro_t *duplicate() const {
bool hasSwiftInitializer = flags & RO_HAS_SWIFT_INITIALIZER;
size_t size = sizeof(*this);
if (hasSwiftInitializer)
size += sizeof(_swiftMetadataInitializer_NEVER_USE[0]);
class_ro_t *ro = (class_ro_t *)memdup(this, size);
if (hasSwiftInitializer)
ro->_swiftMetadataInitializer_NEVER_USE[0] = this->_swiftMetadataInitializer_NEVER_USE[0];
#if __has_feature(ptrauth_calls)
// Re-sign the method list pointer if it was signed.
// NOTE: It is possible for a signed pointer to have a signature
// that is all zeroes. This is indistinguishable from a raw pointer.
// This code will treat such a pointer as signed and re-sign it. A
// false positive is safe: method list pointers are either authed or
// stripped, so if baseMethods() doesn't expect it to be signed, it
// will ignore the signature.
void *strippedBaseMethodList = ptrauth_strip(baseMethodList, ptrauth_key_method_list_pointer);
void *signedBaseMethodList = ptrauth_sign_unauthenticated(strippedBaseMethodList,
ptrauth_key_method_list_pointer,
baseMethodListPtrauthData());
if (baseMethodList == signedBaseMethodList) {
ro->baseMethodList = ptrauth_auth_and_resign(baseMethodList,
ptrauth_key_method_list_pointer,
baseMethodListPtrauthData(),
ptrauth_key_method_list_pointer,
ro->baseMethodListPtrauthData());
} else {
// Special case: a class_ro_t in the shared cache pointing to a
// method list in the shared cache will not have a signed pointer,
// but the duplicate will be expected to have a signed pointer since
// it's not in the shared cache. Detect that and sign it.
bool roInSharedCache = objc::inSharedCache((uintptr_t)this);
bool listInSharedCache = objc::inSharedCache((uintptr_t)strippedBaseMethodList);
if (roInSharedCache && listInSharedCache)
ro->baseMethodList = ptrauth_sign_unauthenticated(strippedBaseMethodList,
ptrauth_key_method_list_pointer,
ro->baseMethodListPtrauthData());
}
#endif
return ro;
}
Class getNonMetaclass() const {
ASSERT(flags & RO_META);
return nonMetaclass;
}
const uint8_t *getIvarLayout() const {
if (flags & RO_META)
return nullptr;
return ivarLayout;
}
};
總結(jié):
寫到這里,篇幅有些長了。我們發(fā)現(xiàn)objc2中類的數(shù)據(jù)結(jié)構(gòu)的相比較之前的版本更為復(fù)雜,層級也比較深。
- object_class嵌套了class_data_bits_t類型的結(jié)構(gòu)體bits
- 結(jié)構(gòu)體bits只有一個uintptr_t類型的成員bits,但作為c++結(jié)構(gòu)體,它擴展一些類的存取方法,在成員bits有限的bit位,存儲了指向class_rw_t結(jié)構(gòu)體指針,以及一些其他的信息比如:hasCustomRR、swift相關(guān)。
- class_rw_t包含了flag,witness,firstSubClass,nextsiblingClass等成員以及最重要的一個共用體指針ro_or_rw_ext。
- ro_or_rw_ext可以是class_ro_t類型,也可以是class_rw_ext_t類型。
- class_rw_ext_t包含ro、方法列表,屬性列表、協(xié)議列表等
- class_ro_t不僅包含方法列表、屬性列表、協(xié)議列表,還有一些其他的類相關(guān)內(nèi)容。
那么class_ro_t和class_rw_ext_t有什么區(qū)別?相對以前版本,為什么蘋果給類的數(shù)據(jù)結(jié)構(gòu)設(shè)計這么多的層級呢?rw 、 rw_ext、ro的作用分別是什么?聽下回分解吧。