脚本宝典收集整理的这篇文章主要介绍了Objective-C runtime 拾遗 (三)——Block冷知识,脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
动因
上次写代码时需要深入了解Block。发现Block is nothing but a struct
。今天又拾一下牙慧,汇总一下资料。顺便记录几个源码中的发现
值得读的参考
最好的文档
Clang
中文的话,这篇也够了,讲得比较细:
谈Objective-C block的实现
这篇也讲解得不错:
Block技巧与底层解析
另外跟本文无关的,这个人的blog很不错,很多底层知识。
mikeash
参考代码写在前面
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中的isa
这也是为什么Block能当做id类型的参数进行传递。
但如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
,实际情况是(模拟器):
(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
}
分析之后的结果是这样的:
所有stack Block
的isa
都指向_NSConcreteStackBlock,runtime的时候已经初始化了。何时初始化未知。
_NSConcreteStackBlock是个数组,size为32
元素都是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);
关于flags
除了标示Block的类型,还用作reference counting:
volatile int32_t flags; // contains ref count
因为最低位被BLOCK_DEALLOCATING
使用了,所以ref count每次+2
关于invoke
看代码:
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 t@R_406_2570@ 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.
Runtime Helper Functions
源码的注释这样说的:
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 variables
In 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 链接
以上是脚本宝典为你收集整理的Objective-C runtime 拾遗 (三)——Block冷知识全部内容,希望文章能够帮你解决Objective-C runtime 拾遗 (三)——Block冷知识所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。