上次寫代碼時須要深刻了解Block。發現Block is nothing but a struct
。今天又拾一下牙慧,彙總一下資料。順便記錄幾個源碼中的發現html
最好的文檔
Clang
中文的話,這篇也夠了,講得比較細:
談Objective-C block的實現
這篇也講解得不錯:
Block技巧與底層解析segmentfault
另外跟本文無關的,這我的的Blog很不錯,不少底層知識。
mikeash數組
源碼:
Block_private.h
runtime.capp
enum { // Flags from BlockLiteral BLOCK_DEALLOCATING = (0x0001), // runtime BLOCK_REFCOUNT_MASK = (0xfffe), // runtime BLOCK_NEEDS_FREE = (1 << 24), // runtime BLOCK_HAS_COPY_DISPOSE = (1 << 25), // compiler BLOCK_HAS_CTOR = (1 << 26), // compiler: helpers have C++ code BLOCK_IS_GC = (1 << 27), // runtime BLOCK_IS_GLOBAL = (1 << 28), // compiler BLOCK_USE_STRET = (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE BLOCK_HAS_SIGNATURE = (1 << 30), // compiler BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31) // compiler }; #define BLOCK_DESCRIPTOR_1 1 struct Block_descriptor_1 { uintptr_t reserved; uintptr_t size; }; #define BLOCK_DESCRIPTOR_2 1 struct Block_descriptor_2 { // requires BLOCK_HAS_COPY_DISPOSE void (*copy)(void *dst, const void *src); void (*dispose)(const void *); }; #define BLOCK_DESCRIPTOR_3 1 struct Block_descriptor_3 { // requires BLOCK_HAS_SIGNATURE const char *signature; const char *layout; // contents depend on BLOCK_HAS_EXTENDED_LAYOUT }; struct Block_layout { void *isa; volatile int32_t flags; // contains ref count int32_t reserved; void (*invoke)(void *, ...); struct Block_descriptor_1 *descriptor; // imported variables };
這也是爲何Block能當作id類型的參數進行傳遞。ide
但如Clang文檔中所述:函數
The isa field is set to the address of the external _NSConcreteStackBlock, which is a block of uninitialized memory supplied in libSystem, or _NSConcreteGlobalBlock if this is a static or file level Block literal.
首先_NSConcreteStackBlock是外部的,是libSystem提供的地址,a block of uninitialized memory
,實際狀況是(模擬器):ui
(lldb) p &_NSConcreteStackBlock (void *(*)[32]) $25 = 0x00000001108dc050 (lldb) p _NSConcreteStackBlock (void *[32]) $26 = { [0] = 0x00000001108dc0d0 [1] = 0x000000010e2e6000 [2] = 0x00007f8b88d0d100 [3] = 0x0000000400000007 [4] = 0x00007f8b8aa00310 [5] = 0x0000000000000000 [6] = 0x0000000000000000 [7] = 0x0000000000000000 [8] = 0x0000000000000000 [9] = 0x0000000000000000 [10] = 0x0000000000000000 [11] = 0x0000000000000000 [12] = 0x0000000000000000 [13] = 0x0000000000000000 [14] = 0x0000000000000000 [15] = 0x0000000000000000 [16] = 0x000000010de91198 [17] = 0x000000010e2e5fd8 [18] = 0x000000010db4cf70 [19] = 0x0000000000000000 [20] = 0x00007f8b8aa00350 [21] = 0x0000000000000000 [22] = 0x0000000000000000 [23] = 0x0000000000000000 [24] = 0x0000000000000000 [25] = 0x0000000000000000 [26] = 0x0000000000000000 [27] = 0x0000000000000000 [28] = 0x0000000000000000 [29] = 0x0000000000000000 [30] = 0x0000000000000000 [31] = 0x0000000000000000 }
分析以後的結果是這樣的:this
全部stack Block
的isa
都指向_NSConcreteStackBlock,runtime的時候已經初始化了。什麼時候初始化未知。rest
_NSConcreteStackBlock是個數組,size爲32code
水平有限,沒看出規律。應該是被平分紅兩部分,第二部分從_NSConcreteStackBlock[16]
開始
元素都是Class(廢話),如__NSStackBlock__
,__NSStackBlock
_NSConcreteGlobalBlock
相似
代碼也說明了這一點:
BLOCK_EXPORT void * _NSConcreteMallocBlock[32] __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2); BLOCK_EXPORT void * _NSConcreteAutoBlock[32] __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2); BLOCK_EXPORT void * _NSConcreteFinalizingBlock[32] __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2); BLOCK_EXPORT void * _NSConcreteWeakBlockVariable[32] __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
除了標示Block的類型,還用做reference counting:volatile int32_t flags; // contains ref count
由於最低位被BLOCK_DEALLOCATING
使用了,因此ref count每次+2
看代碼:
id b = ^(int n, double d, char* s){ NSLog(@"%d %lf %s",n, d, s); }; ((__bridge struct Block_layout*)(b))->invoke((__bridge void *)(b),1,2.345,"hello");
官方解釋:
The invoke function pointer is set to a function that takes the Block structure as its first argument and the rest of the arguments (if any) to the Block and executes the Block compound statement.
源碼的註釋這樣說的:
A Block can reference four different kinds of things that require help when the Block is copied to the heap.
1) C++ stack based objects
2) References to Objective-C objects
3) Other Blocks
4) __block variablesIn these cases helper functions are synthesized by the compiler for use in Block_copy and Block_release, called the copy and dispose helpers. The copy helper emits a call to the C++ const copy constructor for C++ stack based objects and for the rest calls into the runtime support function _Block_object_assign. The dispose helper has a call to the C++ destructor for case 1 and a call into _Block_object_dispose for the rest.
簡單來講,當Block引用了 1) C++ 棧上對象
2)OC對象
3) 其餘block對象
4) __block修飾的變量
,並被拷貝至堆上時則須要copy/dispose輔助函數。輔助函數由編譯器生成。
除了case 1,其餘三種case都會分別調用下面的函數:
void _Block_object_assign(void *destAddr, const void *object, const int flags); void _Block_object_dispose(const void *object, const int flags);
_Block_object_assign
(或者_Block_object_dispose
)會根據flags的值來決定調用相應類型的copy helper
(或者dispose helper
)
例如:
switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) { case BLOCK_FIELD_IS_OBJECT: _Block_retain_object(object); _Block_assign((void *)object, destAddr); break; ... }
上述代碼中_Block_retain_object
_Block_assign
以SPI的形式提供給runtime 和 CoreFoundation進行注入。
原做寫於segmentfault 連接