上次写代码时须要深刻了解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 连接