開發中常常使用 NSObject *object = [[NSObject alloc] init];
這行代碼去建立一個對象,那 alloc
和 init
分別幹了些什麼事情呢?bash
下方就是對 alloc
的一個初探過程,也會發現不少有意思的事情。函數
先上一張 alloc
的流程圖。post
而後咱們還須要 objc-752
的源碼,具體的配置過程參考 Cooci
大大的 這篇文章。優化
能運行 objc-752
源碼後,在 main.m
中寫入以下代碼就開始了 alloc
的探索。ui
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSObject *object = [[NSObject alloc] init];
NSLog(@"Hello, World! %@",object);
}
return 0;
}
複製代碼
commend
點擊 alloc
就能夠進入源碼進行查看了。this
注意:這裏 alloc
點擊進入的爲 _objc_rootAlloc
其實咱們斷點後發現進入的是 objc_alloc
。spa
alloc
直接進入 objc_alloc
的緣由補充。指針
id
objc_alloc(Class cls)
{
return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
複製代碼
當前傳入了一個須要構建類的結構體 Class
,是一個 objc_class
的類型,存儲了一些當前類的信息。code
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() {
return bits.data();
}
//...
}
複製代碼
objc_class
又繼承於 objc_class
,objc_class
存儲了 isa
的一個 Class
結構體,指向當前是什麼類。orm
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;
複製代碼
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
/**
* bool(x) 爲假的可能性更大 else 執行的機率會更大
* #define slowpath(x) (__builtin_expect(bool(x), 0))
*/
if (slowpath(checkNil && !cls)) return nil;
#if __OBJC2__
//這是判斷一個類是否有自定義的 +allocWithZone 實現。
//hasCustomAWZ : hasCustomAllocWithZone
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
// No alloc/allocWithZone implementation. Go straight to the allocator.
// fixme store hasCustomAWZ in the non-meta class and
// add it to canAllocFasts summary
//這裏是沒有 alloc / allocWithZone 的實現,走的初始化器,
//而且 沒有元類,這裏說明的是否是繼承 NSObject /NSProxy 的時候纔會進入canAllocFast
if (fastpath(cls->canAllocFast())) {
// No ctors, raw isa, etc. Go straight to the metal.
bool dtor = cls->hasCxxDtor();
id obj = (id)calloc(1, cls->bits.fastInstanceSize());
if (slowpath(!obj)) return callBadAllocHandler(cls);
obj->initInstanceIsa(cls, dtor);
return obj;
}
else {
// Has ctor or raw isa or something. Use the slower path.
id obj = class_createInstance(cls, 0);
if (slowpath(!obj)) return callBadAllocHandler(cls);
return obj;
}
}
#endif
// No shortcuts available.
if (allocWithZone) return [cls allocWithZone:nil];
return [cls alloc];
}
複製代碼
第一句 if (slowpath(checkNil && !cls)) return nil;
其實就是判斷了當前 cls
是否存在。
__builtin_expect
經常使用於 if-else
的判斷爲了優化判斷的速度。
__builtin_expect(x,1)
表明 x
爲真的可能性更大,if
下的代碼執行的可能性更高__builtin_expect(x,0)
表明 x
爲假的可能性更大,else
下的代碼執行的可能性更高//bool(x) 爲真的可能性更大
#define fastpath(x) (__builtin_expect(bool(x), 1))
//bool(x) 爲假的可能性更大
#define slowpath(x) (__builtin_expect(bool(x), 0))
複製代碼
hasCustomAWZ
其實就是 hasCustomAllocWithZone
的意思。
if (fastpath(!cls->ISA()->hasCustomAWZ()))
這裏是沒有 alloc / allocWithZone
的實現,走的初始化器,而且沒有元類,這裏說明的是否是繼承 ·NSObject / NSProxy` 類的時候纔會進入。
if (fastpath(cls->canAllocFast())) {
// No ctors, raw isa, etc. Go straight to the metal.
bool dtor = cls->hasCxxDtor();
id obj = (id)calloc(1, cls->bits.fastInstanceSize());
if (slowpath(!obj)) return callBadAllocHandler(cls);
obj->initInstanceIsa(cls, dtor);
return obj;
}
複製代碼
這裏的大概意思是:沒有類,沒有 isa
指針纔會進入,這裏有點不太明白。可是查詢 canAllocFast()
的實現的時候發現 canAllocFast()
永遠返回 false
。
else
{
// Has ctor or raw isa or something. Use the slower path.
id obj = class_createInstance(cls, 0);
if (slowpath(!obj)) return callBadAllocHandler(cls);
return obj;
}
複製代碼
else
就進入了建立過程了。
函數原型能夠看到,傳入了當前類的Class
和一個 extraBytes
額外須要的內存大小 0
。
id
class_createInstance(Class cls, size_t extraBytes)
{
return _class_createInstanceFromZone(cls, extraBytes, nil);
}
複製代碼
_class_createInstanceFromZone
是 alloc
的全部執行操做了,包括類大小的獲取,類的堆空間開闢,isa
指針的指向。
static __attribute__((always_inline))
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
if (!cls) return nil;
assert(cls->isRealized());
// Read class info bits all at once for performance
//hasCxxCtor() 是判斷當前 class 或者 superclass 是否有 .cxx_construct 構造方法的實現。
bool hasCxxCtor = cls->hasCxxCtor();
//hasCxxDtor() 是判斷判斷當前 class 或者 superclass 是否有 .cxx_destruct 析構方法的實現。
bool hasCxxDtor = cls->hasCxxDtor();
//具體標記某個類是否支持優化的isa.
bool fast = cls->canAllocNonpointer();
//獲取類的大小 (傳入額外字節的大小)
size_t size = cls->instanceSize(extraBytes);
//若是傳入分配大小就須要修改
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (!zone && fast) {
/**
* void *calloc(size_t __count, size_t __size)
* 在內存的動態存儲區中分配 __count 個長度爲 __size 的連續空間
*/
obj = (id)calloc(1, size);
if (!obj) return nil;
obj->initInstanceIsa(cls, hasCxxDtor);
}
else {
if (zone) {
obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
if (!obj) return nil;
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
if (cxxConstruct && hasCxxCtor) {
obj = _objc_constructOrFree(obj, cls);
}
return obj;
}
複製代碼
static __attribute__
的標識表明 OC
中 C++
的全局構造函數。
bool hasCxxCtor = cls->hasCxxCtor();
hasCxxCtor()
是判斷當前 class
或者 superclass
是否有 .cxx_construct
構造方法的實現。
bool hasCxxDtor = cls->hasCxxDtor();
hasCxxDtor()
是判斷判斷當前 class
或者 superclass
是否有 .cxx_destruct
析構方法的實現。
bool fast = cls->canAllocNonpointer();
具體標記某個類是否支持優化的isa.
bool canAllocNonpointer() {
assert(!isFuture());
//實例是否須要原始Isa instancesRequireRawIsa = NO
return !instancesRequireRawIsa();//YES
}
複製代碼
size_t size = cls->instanceSize(extraBytes);
獲取類的大小(傳入額外字節的大小)
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
//CF 最小16字節
if (size < 16) size = 16;
return size;
}
複製代碼
在 unalignedInstanceSize
方法中 data()->ro->instanceSize;
獲取類所佔用空間的大小,實際上是在 MacO
的 data
段的 ro
中的獲取類所佔用的大小。
這裏有一些有意思的事情,好比 字節對齊,CoreFoundation
規定字節數最小爲 16
。
關於字節對齊:OC
是 8
字節對齊,在 word_align
這個方法中計算了字節對齊。
static inline uint32_t word_align(uint32_t x) {
//字節對齊
return (x + WORD_MASK) & ~WORD_MASK;
}
複製代碼
計算過程以下:
假如: x = 9
x + WORD_MASK = 9 + 7 = 16
WORD_MASK 二進制 :0000 0111 = 7 (4+2+1)
~WORD_MASK : 1111 1000
16二進制爲 : 0001 0000
1111 1000
0001 0000
---------------
0001 0000 = 16
因此 x = 16(原始值:9) 也就是 8的倍數對齊,即 8 字節對齊
複製代碼
上方的計算過程就能看到 OC
採用了 8 字節對齊。
if (outAllocatedSize) *outAllocatedSize = size;
若是傳入分配大小就須要使用傳入的,可是上方默認值爲 nil
( size_t *outAllocatedSize = nil
)。
而後調用 void *calloc(size_t __count, size_t __size)
在內存的動態存儲區中分配 __count
個長度爲 __size
的連續空間。
獲取到了 obj
而後 使用 obj->initInstanceIsa(cls, hasCxxDtor);
綁定 isa
指針,說明這塊內存空間是爲誰開闢的。
最後返回 obj
。
這就是一個對象的初始化過程。
咦,那 init
到底幹嗎了??
其實 init
什麼也沒有幹,就是爲了提供一個自定義接口,方便開發者本身實現一些在初始化須要乾的事情。
// Replaced by CF (throws an NSException)
+ (id)init {
return (id)self;
}
- (id)init {
return _objc_rootInit(self);
}
id
_objc_rootInit(id obj)
{
// In practice, it will be hard to rely on this function.
// Many classes do not properly chain -init calls.
return obj;
}
複製代碼
以上就是對 alloc 的初探
的內容了。