Objective-C 對象的本質(zhì) 01 - 底層實(shí)現(xiàn)
我們平時編寫的 Objective-C 代碼,底層實(shí)現(xiàn)為 C/C++ 代碼迁酸。
Objective-C --> C/C++ --> 匯編 --> 機(jī)器碼讲冠。
Objective-C 的面向?qū)ο笫腔?C/C++ 的==結(jié)構(gòu)體==實(shí)現(xiàn)的瓜客。
在終端里使用 ==xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc class.m -o class.cpp== 命令可以將 Objective-C 轉(zhuǎn)換為 C++ 代碼。
Objective-C 底層實(shí)現(xiàn):
// Objective-C
@interface NSObject {
Class isa;
}
@end
// C++: NSObject Implementation
// 存儲了 Class 對象的 isa 和 其他成員變量
struct NSObject_IMPL {
Class isa; // 8 Byte
};
// Class 指針
typedef struct objc_class *Class;
// id竿开,可以作為任何對象的指針
typedef struct objc_object *id;
// Tips:子類的的結(jié)構(gòu)體實(shí)現(xiàn)中有一個成員為父類的結(jié)構(gòu)體
struct Bar_IMPL {
struct Foo_IMPL Foo_IVARS;
// ivars...
}
一個 Objective-C(假設(shè)沒有屬性) 對象占用多少內(nèi)存谱仪?
- 先拋結(jié)論:一個沒有屬性的 Objective-C 對象占用 16 Byte 的內(nèi)存空間。
- 使用 class_getInstanceSize() 函數(shù)可以查看一個類底層結(jié)構(gòu)體占用的內(nèi)存大蟹癫省(結(jié)構(gòu)體需要內(nèi)存對齊)疯攒,返回 8 Byte。
- 使用 malloc_size() 函數(shù)可以查看一個對象占用的內(nèi)存大小列荔,16 Byte敬尺。
試驗(yàn):在類中不斷添加 int 屬性(4 Byte),再次調(diào)用 class_getInstanceSize() 和 malloc_size()贴浙。
- 添加 1 ~ 2 個 int 屬性:
- class_getInstanceSize():return 16 Byte砂吞,
- malloc_size():return 16 Byte,
- 添加 3 ~ 4 個 int 屬性:
- class_getInstanceSize():return 24 Byte崎溃,
- malloc_size():return 32 Byte蜻直,
- 添加 5 ~ 6 個 int 屬性:
- class_getInstanceSize():return 32 Byte,
- malloc_size():return 32 Byte笨奠,
- 添加 7 ~ 8 個 int 屬性:
- class_getInstanceSize():return 40 Byte袭蝗,
- malloc_size():return 48 Byte,
結(jié)論:
- class_getInstanceSize():在添加 int 屬性的過程中般婆,NSObject_IMPL 結(jié)構(gòu)體中寬度最大的元素始終為 isa 指針變量到腥,所以結(jié)構(gòu)體的大小為 8 Byte 的整倍數(shù) 。
- malloc_size():實(shí)際在為對象分配內(nèi)存空間時蔚袍,大小為 16 Byte 的整倍數(shù)乡范。
補(bǔ)充:通過在控制臺使用 memory read 指令打印對象的內(nèi)存數(shù)據(jù)配名,也可以看到長度為 16 Byte。
訪問內(nèi)存的 LLDB 命令:
通過上面的試驗(yàn)可以知道晋辆,實(shí)際上在實(shí)例化一個對象時渠脉,需要分配至少 16 Byte 的內(nèi)存空間,接下來看看相關(guān)的源碼瓶佳。
在 objc4-723 版本的源碼中芋膘,從 objc/Project-Headers/objc-runtime-new.h 文件中的 objc_class 結(jié)構(gòu)體中,有一個 instanceSize() 函數(shù)(1279 ~ 1284 行)霸饲,返回值 >= 16为朋,1281 行的注釋為:“// CF requires all objects be at least 16 bytes.”,通過跟蹤類的 alloc 方法內(nèi)部的調(diào)用鏈厚脉,可以看到在分配內(nèi)存前調(diào)用了 instanceSize() 函數(shù)习寸,calloc() 函數(shù)根據(jù)這個函數(shù)的返回值分配內(nèi)存空間給對象使用,并且會保證內(nèi)存大小為 16 Byte 的整倍數(shù)傻工。
class_getInstanceSize() 函數(shù)在蘋果開源的 objc4 源碼中可以看到霞溪。
// objc/Source/objc-class.mm // // 查看一個類底層結(jié)構(gòu)體占用的內(nèi)存大小(結(jié)構(gòu)體需要內(nèi)存對齊) size_t class_getInstanceSize(Class cls) { if (!cls) return 0; return cls->alignedInstanceSize(); // 調(diào)用 objc_class 結(jié)構(gòu)體中的函數(shù) }
核心源碼
objc_object中捆、objc_class
// objc/Project-Headers/objc-private.h
//
// objc_class 的基類鸯匹,主要存儲了 Meta-Class 對象的 isa
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
// 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);
};
// objc/Project-Headers/objc-runtime-new.h
//
// Class、Meta-Class 都是通過 objc_class 實(shí)現(xiàn)的
//
// 作為 Class 使用時泄伪,存儲了:
// isa:Meta-Class 對象的 isa
// superclass:父類的 Class 對象忽你,如果沒有父類,superclass 為 nil
// bits.data().ro.ivars:成員變量信息(ivar_list_t)
// bits.data().properties:屬性信息(method_array_t)
// bits.data().methods:成員方法信息 (property_array_t)
// bits.data().protocols:協(xié)議信息(protocol_array_t)
//
// 作為 Meta-Class 使用時臂容,存儲了:
// isa:基類的 Meta-Class 對象的 isa科雳,基類的 Meta-Class 對象的 isa 指向自身
// superclass:父類的 Meta-Class 對象,
// 基類的 Meta-Class 對象的 superclass 指向基類的 Class 對象
// bits.data().methods:類方法的信息
//
struct objc_class : objc_object {
// Class ISA; // isa脓杉,繼承自 objc_object
Class superclass; // 父類的 Class 對象或 Meta-Class 對象
cache_t cache; // 方法緩存
class_data_bits_t bits; // 存放 Class 或 Meta-Class 信息對象的指針
class_rw_t *data() {
return bits.data();
}
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
// May be unaligned depending on class's ivars.
uint32_t unalignedInstanceSize() {
assert(isRealized());
return data()->ro->instanceSize;
}
// Locking: To prevent concurrent realization, hold runtimeLock.
bool isRealized() {
return data()->flags & RW_REALIZED;
}
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
}
};
RW_REALIZED糟秘、word_align()
// objc/Project-Headers/objc-runtime-new.h
//
// Values for class_rw_t->flags
// These are not emitted by the compiler and are never used in class_ro_t.
// Their presence should be considered in future ABI versions.
// class_t->data is class_rw_t, not class_ro_t
#define RW_REALIZED (1<<31)
// objc/Project-Headers/objc-os.h
//
#ifdef __LP64__
# define WORD_SHIFT 3UL
# define WORD_MASK 7UL
# define WORD_BITS 64
#else
# define WORD_SHIFT 2UL
# define WORD_MASK 3UL
# define WORD_BITS 32
#endif
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
static inline size_t word_align(size_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
class_data_bits_t、class_rw_t球散、class_ro_t
// objc/Project-Headers/objc-runtime-new.h
// data pointer
#define FAST_DATA_MASK 0x00007ffffffffff8UL
// objc/Project-Headers/objc-runtime-new.h
//
// 存放類信息對象的指針
struct class_data_bits_t {
// Values are the FAST_ flags above.
uintptr_t bits; // typedef unsigned long uintptr_t;
class_rw_t* data() {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
};
// objc/Project-Headers/objc-runtime-new.h
//
// 存放類的成員(變量尿赚、屬性、方法)信息蕉堰、協(xié)議信息或類方法信息凌净,包含分類中的屬性、方法屋讶、協(xié)議信息
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods; // 成員方法信息或類方法信息(包含分類的方法信息)
property_array_t properties; // 屬性信息(包含分類的屬性信息)
protocol_array_t protocols; // 協(xié)議信息(包含分類的協(xié)議信息)
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
#if SUPPORT_INDEXED_ISA
uint32_t index;
#endif
};
// objc/Project-Headers/objc-runtime-new.h
//
// 存放類的成員(變量冰寻、屬性、方法)信息皿渗、協(xié)議信息或類方法信息斩芭,不包含分類中的屬性轻腺、方法、協(xié)議信息
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize; // 類底層結(jié)構(gòu)體占用的內(nèi)存大小
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name; // 類名
method_list_t * baseMethodList; // 成員方法信息或類方法信息(不包含分類的方法信息)
protocol_list_t * baseProtocols;// 協(xié)議信息(不包含分類的協(xié)議信息)
const ivar_list_t * ivars; // 成員變量信息
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;// 屬性信息(不包含分類的屬性信息)
method_list_t *baseMethods() const {
return baseMethodList;
}
};
SEL划乖、IMP
// 方法名(選擇器)
//
// 可以通過 @selector() 或 sel_registerName() 獲取 SEL贬养。
// 可以通過 sel_getName() 或 NSStringFromSelector() 將 SEL 轉(zhuǎn)換為字符串。
// 不同類中的同名方法所對應(yīng)的 SEL 是相同的琴庵。
//
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
// 函數(shù)指針
//
/// A pointer to the function of a method implementation.
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...);
#endif
bucket_t误算、cache_t
typedef unsigned long uintptr_t;
// 方法緩存中的 Key
#if __LP64__
typedef uint32_t mask_t; // x86_64 & arm64 asm are less efficient with 16-bits
#else
typedef uint16_t mask_t;
#endif
typedef uintptr_t cache_key_t;
// 方法緩存
struct bucket_t {
private:
cache_key_t _key; // key 是方法的 SEL
IMP _imp; // 函數(shù)指針
public:
inline cache_key_t key() const { return _key; }
inline IMP imp() const { return (IMP)_imp; }
inline void setKey(cache_key_t newKey) { _key = newKey; }
inline void setImp(IMP newImp) { _imp = newImp; }
void set(cache_key_t newKey, IMP newImp);
};
// 方法緩存列表
//
// 用散列表(哈希表)來緩存調(diào)用多的方法,可以提高查找方法的速度迷殿。
//
// _mask 為什么是 _buckets 的長度 -1 ?
// -1 能確保 (key & _mask) <= (_buckets 的長度 - 1)尉桩,用來作為 _buckets 的下標(biāo)來取值。
// 在 cache_t::find() 函數(shù)中可以看到計(jì)算下標(biāo)的代碼贪庙。
//
// _buckets 的動態(tài)擴(kuò)容:
// 向 _buckets 添加最后一個緩存時會進(jìn)行擴(kuò)容。
// 每次擴(kuò)容為原來的兩倍(2^n)翰苫。
// 擴(kuò)容時會將緩存清除止邮,因?yàn)閿U(kuò)容后 _mask 發(fā)生了變化。
// 通過 cache_t::expand() 函數(shù)中進(jìn)行擴(kuò)容奏窑。
struct cache_t {
struct bucket_t *_buckets; // 散列表
mask_t _mask; // 散列表的長度 -1
mask_t _occupied; // 已經(jīng)緩存的方法數(shù)量(<= _mask)
public:
struct bucket_t *buckets();
mask_t mask();
mask_t occupied();
void incrementOccupied();
void setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask);
void initializeToEmpty();
mask_t capacity();
bool isConstantEmptyCache();
bool canBeFreed();
static size_t bytesForCapacity(uint32_t cap);
static struct bucket_t * endMarker(struct bucket_t *b, uint32_t cap);
void expand();
void reallocate(mask_t oldCapacity, mask_t newCapacity);
struct bucket_t * find(cache_key_t key, id receiver);
static void bad_cache(id receiver, SEL sel, Class isa) __attribute__((noreturn));
};
其它源碼
entsize_list_tt导披、list_array_tt
// 數(shù)組,ivar_list_t埃唯、method_list_t撩匕、property_list_t 都是 entsize_list_tt 的子類型。
/***********************************************************************
* entsize_list_tt<Element, List, FlagMask>
* Generic implementation of an array of non-fragile structs.
*
* Element is the struct type (e.g. method_t)
* List is the specialization of entsize_list_tt (e.g. method_list_t)
* FlagMask is used to stash extra bits in the entsize field
* (e.g. method list fixup markers)
**********************************************************************/
template <typename Element, typename List, uint32_t FlagMask>
struct entsize_list_tt {
uint32_t entsizeAndFlags;
uint32_t count;
Element first;
uint32_t entsize() const;
uint32_t flags() const;
Element& getOrEnd(uint32_t i) const;
Element& get(uint32_t i) const;
size_t byteSize() const;
List* duplicate() const;
struct iterator;
const iterator begin() const;
const iterator end() const;
struct iterator {
uint32_t entsize;
uint32_t index; // keeping track of this saves a divide in operator-
Element* element;
iterator() { }
iterator(const List& list, uint32_t start = 0)
: entsize(list.entsize())
, index(start)
, element(&list.getOrEnd(start))
{ }
};
};
// 數(shù)組墨叛,property_array_t止毕、method_array_t、protocol_array_t 都是 list_array_tt 的子類型漠趁。
/***********************************************************************
* list_array_tt<Element, List>
* Generic implementation for metadata that can be augmented by categories.
*
* Element is the underlying metadata type (e.g. method_t)
* List is the metadata's list type (e.g. method_list_t)
*
* A list_array_tt has one of three values:
* - empty
* - a pointer to a single list
* - an array of pointers to lists
*
* countLists/beginLists/endLists iterate the metadata lists
* count/begin/end iterate the underlying metadata elements
**********************************************************************/
template <typename Element, typename List>
class list_array_tt {
struct array_t {
uint32_t count;
List* lists[0];
static size_t byteSize(uint32_t count) {
return sizeof(array_t) + count * sizeof(lists[0]);
}
size_t byteSize() {
return byteSize(count);
}
};
protected:
class iterator {
List **lists;
List **listsEnd;
typename List::iterator m, mEnd;
};
public:
uint32_t count();
iterator begin();
iterator end();
uint32_t countLists();
List** beginLists();
List** endLists();
template<typename Result>
Result duplicate();
};
ivar_t扁凛、ivar_list_t
// 成員變量信息
struct ivar_t {
int32_t *offset;
const char *name; // 變量名
const char *type; // 數(shù)據(jù)類型
uint32_t alignment_raw;
uint32_t size; // 占用內(nèi)存大小
uint32_t alignment() const {
if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
return 1 << alignment_raw;
}
};
struct ivar_list_t : entsize_list_tt<ivar_t, ivar_list_t, 0> {
bool containsIvar(Ivar ivar) const {
return (ivar >= (Ivar)&*begin() && ivar < (Ivar)&*end());
}
};
method_t、method_list_t闯传、method_array_t
// 方法信息
//
// *types 示意:| 返回值 | 參數(shù)1 | 參數(shù)2 | ... | 參數(shù)n |
// 例如一個沒有返回值且沒有參數(shù)的方法谨朝,它的 method_t 中的 *types 為 v16@:8
// | v16 | @0 | :8 |
// | void | id | SEL |
// 兩個參數(shù)為默認(rèn)參數(shù),id 對應(yīng) self甥绿,SEL 對應(yīng) _cmd
//
// *types 中的數(shù)字:
// 第一個數(shù)字:所有參數(shù)一共占用多少字節(jié)
// 后面的數(shù)字:當(dāng)前參數(shù)從第幾個字節(jié)開始
struct method_t {
SEL name; // 方法名
const char *types; // 包含了返回值類型字币、參數(shù)類型的編碼字符串
IMP imp; // 函數(shù)指針
};
// 方法信息列表(一維數(shù)組)
// Two bits of entsize are used for fixup markers.
struct method_list_t : entsize_list_tt<method_t, method_list_t, 0x3> {
bool isFixedUp() const;
void setFixedUp();
uint32_t indexOfMethod(const method_t *meth) const {
uint32_t i = (uint32_t)(((uintptr_t)meth - (uintptr_t)this) / entsize());
assert(i < count);
return i;
}
};
// 方法信息列表(二維數(shù)組)
class method_array_t : public list_array_tt<method_t, method_list_t> {
typedef list_array_tt<method_t, method_list_t> Super;
public:
method_list_t **beginCategoryMethodLists() {
return beginLists();
}
method_list_t **endCategoryMethodLists(Class cls);
method_array_t duplicate() {
return Super::duplicate<method_array_t>();
}
};
Type Encoding(通過 @encode() 可以查看指定類型的字符串編碼)
property_t、property_list_t共缕、property_array_t
struct property_t {
const char *name;
const char *attributes;
};
struct property_list_t : entsize_list_tt<property_t, property_list_t, 0> {
};
class property_array_t : public list_array_tt<property_t, property_list_t> {
typedef list_array_tt<property_t, property_list_t> Super;
public:
property_array_t duplicate() {
return Super::duplicate<property_array_t>();
}
};
protocol_t洗出、protocol_list_t、protocol_array_t
typedef uintptr_t protocol_ref_t; // protocol_t *, but unremapped
// Values for protocol_t->flags
#define PROTOCOL_FIXED_UP_2 (1<<31) // must never be set by compiler
#define PROTOCOL_FIXED_UP_1 (1<<30) // must never be set by compiler
// Bits 0..15 are reserved for Swift's use.
#define PROTOCOL_FIXED_UP_MASK (PROTOCOL_FIXED_UP_1 | PROTOCOL_FIXED_UP_2)
struct protocol_t : objc_object {
const char *mangledName;
struct protocol_list_t *protocols;
method_list_t *instanceMethods;
method_list_t *classMethods;
method_list_t *optionalInstanceMethods;
method_list_t *optionalClassMethods;
property_list_t *instanceProperties;
uint32_t size; // sizeof(protocol_t)
uint32_t flags;
// Fields below this point are not always present on disk.
const char **_extendedMethodTypes;
const char *_demangledName;
property_list_t *_classProperties;
const char *demangledName();
const char *nameForLogging() {
return demangledName();
}
bool isFixedUp() const;
void setFixedUp();
# define HAS_FIELD(f) (size >= offsetof(protocol_t, f) + sizeof(f))
bool hasExtendedMethodTypesField() const {
return HAS_FIELD(_extendedMethodTypes);
}
bool hasDemangledNameField() const {
return HAS_FIELD(_demangledName);
}
bool hasClassPropertiesField() const {
return HAS_FIELD(_classProperties);
}
# undef HAS_FIELD
const char **extendedMethodTypes() const {
return hasExtendedMethodTypesField() ? _extendedMethodTypes : nil;
}
property_list_t *classProperties() const {
return hasClassPropertiesField() ? _classProperties : nil;
}
};
struct protocol_list_t {
// count is 64-bit by accident.
uintptr_t count;
protocol_ref_t list[0]; // variable-size
size_t byteSize() const {
return sizeof(*this) + count*sizeof(list[0]);
}
protocol_list_t *duplicate() const {
return (protocol_list_t *)memdup(this, this->byteSize());
}
typedef protocol_ref_t* iterator;
typedef const protocol_ref_t* const_iterator;
const_iterator begin() const {
return list;
}
iterator begin() {
return list;
}
const_iterator end() const {
return list + count;
}
iterator end() {
return list + count;
}
};
class protocol_array_t : public list_array_tt<protocol_ref_t, protocol_list_t> {
typedef list_array_tt<protocol_ref_t, protocol_list_t> Super;
public:
protocol_array_t duplicate() {
return Super::duplicate<protocol_array_t>();
}
};