文章目录
关于 block
块的实现原理,以及对外部变量的拷贝和引用问题。
block文档 做了详细说明,这里再复习一遍,并以自己的理解方式写出。
block文档 描述了块的Apple ABI
实现规范,其实就是编译器对 block
的转化实现,去了解一个 block
声明时,编译器是怎么构建的。
block的结构体
这个ABI
的第一个发行版本是在Mac OS X 10.6
中找到的,称为10.6.ABI
。从2010年3月16日
起,第二个版本,必要时将称为ABI.2010.3.16
编译器会考虑runtime运行时的需要
和各种引用关系
来定义block
的ABI
,也就是说,编译器会根据上下文,引入的参数,来定义不同的ABI
,(我们可以通过-rewrite-objc
来将OC
文件编译成cpp实现
来证实),一个block
的结构体如下:
struct Block_literal_1 {
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor_1 {
unsigned long int reserved; // NULL
unsigned long int size; // sizeof(struct Block_literal_1)
// optional helper functions
void (*copy_helper)(void *dst, void *src); // IFF (1<<25)
void (*dispose_helper)(void *src); // IFF (1<<25)
// required ABI.2010.3.16
const char *signature; // IFF (1<<30)
} *descriptor;
// imported variables
};
isa
可以看到block
被设计成一个对象,可以相应消息。
flag
字段用于表示编译器生成的block
的“类型”
,后面有说明,其枚举如下:
对于 ABI.2010.3.16
enum {
// Set to true on blocks that have captures (and thus are not true
// global blocks) but are known not to escape for various other
// reasons. For backward compatibility with old runtimes, whenever
// BLOCK_IS_NOESCAPE is set, BLOCK_IS_GLOBAL is set too. Copying a
// non-escaping block returns the original block and releasing such a
// block is a no-op, which is exactly how global blocks are handled.
BLOCK_IS_NOESCAPE = (1 << 23),
BLOCK_HAS_COPY_DISPOSE = (1 << 25),
BLOCK_HAS_CTOR = (1 << 26), // helpers have C++ code
BLOCK_IS_GLOBAL = (1 << 28),
BLOCK_HAS_STRET = (1 << 29), // IFF BLOCK_HAS_SIGNATURE
BLOCK_HAS_SIGNATURE = (1 << 30),
};
在10.6.ABI
,(1<<29)
通常被设置,并且总是被runtime
忽略(它是一个转换标记,在转换之后没有被删除
)。该位现在与 (1<<30)
成对出现,并表示为对(3<<29)
,以下是有效位设置的组合,还有其意义:
switch (flags & (3<<29)) {
case (0<<29): 10.6.ABI, no signature field available
case (1<<29): 10.6.ABI, no signature field available
case (2<<29): ABI.2010.3.16, regular calling convention, presence of signature field
case (3<<29): ABI.2010.3.16, stret calling convention, presence of signature field,
}
这里不理解 flag
没事,先接着看。
没有引入变量的block
对于以下 block
块的实现
^ { printf("hello world\n"); }
在32位系统中,编译会将其实现如下:
struct __block_literal_1 {
void *isa;
int flags;
int reserved;
void (*invoke)(struct __block_literal_1 *);
struct __block_descriptor_1 *descriptor;
};
void __block_invoke_1(struct __block_literal_1 *_block) {
printf("hello world\n");
}
static struct __block_descriptor_1 {
unsigned long int reserved;
unsigned long int Block_size;
} __block_descriptor_1 = { 0, sizeof(struct __block_literal_1) };
有了这些“前置”
,这个 block
的声明就如下:
struct __block_literal_1 _block_literal = {
&_NSConcreteStackBlock,
(1<<29), <uninitialized>,
__block_invoke_1,
&__block_descriptor_1
};
可以见得,这里的flag
就是 1<<29
当block
以static常量修饰时,那么它的实现会是:
struct __block_literal_1 __block_literal_1 = {
&_NSConcreteGlobalBlock,
(1<<28)|(1<<29), <uninitialized>,
__block_invoke_1,
&__block_descriptor_1
};
即flag
添加了 BLOCK_IS_GLOBAL
表示这是一个全局的。
引入变量的block
变量如果没有__block
修饰,则会自动拷贝一份const
值到 block
结构体中,不再是之前的那个变量。这也是我们需要修改 block
外的值时,必须使用__block
前缀的原因。
如果使用了__block
修饰,编译器则会将指针复制,并将其包含到一个结构体(这里就叫A
吧)中,然后编译器生成的 block
的结构体就包含这个A
引入常量
int x = 10;
void (^vv)(void) = ^{ printf("x is %d\n", x); }
x = 11;
vv();
这段代码输出则是x is 10
,因为拷贝的是值
,编译器会编译为:
struct __block_literal_2 {
void *isa;
int flags;
int reserved;
void (*invoke)(struct __block_literal_2 *);
struct __block_descriptor_2 *descriptor;
const int x;
};
void __block_invoke_2(struct __block_literal_2 *_block) {
printf("x is %d\n", _block->x);
}
static struct __block_descriptor_2 {
unsigned long int reserved;
unsigned long int Block_size;
} __block_descriptor_2 = { 0, sizeof(struct __block_literal_2) };
可以见得 const int x
则复制了x
的值,此x
非彼x
。
然后 block
结构体的定义:
struct __block_literal_2 __block_literal_2 = {
&_NSConcreteStackBlock,
(1<<29), <uninitialized>,
__block_invoke_2,
&__block_descriptor_2,
x
};
对于这样的常量拷贝形式,是不需要 Runtime Helper Functions
的.
Runtime Helper Functions
就是用来支持runtime
机制的,因为一个对象NSObject
会调用object_release
来释放,而block
赋值的变量指针对象并没有在这个范畴内,所以需要 Runtime Helper Functions
,使得runtime
能够管理其引用关系并及时释放。
引入block
需要copy and dispose helper functions
的第一种情况是在导入block
本身的情况下。在这种情况下,需要copy_helper函数
和dispose_helper函数
。copy_helper
函数既传递现有的基于堆栈的指针
,也传递指向新堆的指针
,它应该回调到runtime
中,对block
中导入的字段
实际执行copy
操作。运行时的方法都是在 Runtime Helper Functions(后续说明)
中描述的。
看下面这个例子
void (^existingBlock)(void) = ...;
void (^vv)(void) = ^{ existingBlock(); }
vv();
struct __block_literal_3 {
...; // existing block
};
struct __block_literal_4 {
void *isa;
int flags;
int reserved;
void (*invoke)(struct __block_literal_4 *);
struct __block_literal_3 *const existingBlock;
};
void __block_invoke_4(struct __block_literal_2 *_block) {
__block->existingBlock->invoke(__block->existingBlock);
}
void __block_copy_4(struct __block_literal_4 *dst, struct __block_literal_4 *src) {
//_Block_copy_assign(&dst->existingBlock, src->existingBlock, 0);
_Block_object_assign(&dst->existingBlock, src->existingBlock, BLOCK_FIELD_IS_BLOCK);
}
void __block_dispose_4(struct __block_literal_4 *src) {
// was _Block_destroy
_Block_object_dispose(src->existingBlock, BLOCK_FIELD_IS_BLOCK);
}
static struct __block_descriptor_4 {
unsigned long int reserved;
unsigned long int Block_size;
void (*copy_helper)(struct __block_literal_4 *dst, struct __block_literal_4 *src);
void (*dispose_helper)(struct __block_literal_4 *);
} __block_descriptor_4 = {
0,
sizeof(struct __block_literal_4),
__block_copy_4,
__block_dispose_4,
};
编译器会编译为:
struct __block_literal_4 _block_literal = {
&_NSConcreteStackBlock,
(1<<25)|(1<<29), <uninitialized>
__block_invoke_4,
& __block_descriptor_4
existingBlock,
};
1<<25
表示 BLOCK_HAS_COPY_DISPOSE
,表示需要辅助函数 helper functions
处理runtime
的引用计数相关(或者更多
)。需要辅助函数就意味着是拷贝指针。
引入 attribute((NSObject)) 变量
GCC
在结构指针
上引入了_attribute__((NSObject))
来表示“这是一个对象”
这很有用,因为许多低层数据结构
都声明为不透明的结构指针
,例如CFStringRef、CFArrayRef
等
例如下面这部分代码,声明一个block foo
struct Opaque *__attribute__((NSObject)) objectPointer = ...;
...
void (^foo)(void) = ^{ CFPrint(objectPointer); };
将会创建helper
方法,这个拷贝的helper function
将由编译器创建,而且应该使用_Block_object_assign
这个运行时方法,然后在释放时,回到用 _Block_object_dispose
,这也是和 runtime
关联的方法,相当于NSObject
对象的引用以及释放。
void __block_copy_foo(struct __block_literal_5 *dst, struct __block_literal_5 *src) {
_Block_object_assign(&dst->objectPointer, src-> objectPointer, BLOCK_FIELD_IS_OBJECT);
}
void __block_dispose_foo(struct __block_literal_5 *src) {
_Block_object_dispose(src->objectPointer, BLOCK_FIELD_IS_OBJECT);
}
引入__block前缀变量
编译器会将 __block
修饰的变量绑定到生成的 block
中,但会包裹一层结构放在 block
中,这个结构就是下面的_block_byref_<name>
:
byref 意思为指针引用
struct _block_byref_foo {
void *isa;
struct Block_byref *forwarding;
int flags; //refcount;
int size;
typeof(marked_variable) marked_variable;
};
当在引用的block
上执行Block_copy()
和Block_release()
时,某些类型的变量需要帮助函数 helper functions
。
在“C”
级别,只有类型为Block
的变量或标记有_attribute__((NSObject))
的变量需要辅助函数
。在Objective-C
中,对象
需要辅助函数
,而在c++
中,基于堆栈
的对象
需要辅助函数
。需要辅助函数
的变量
使用以下形式:
辅助函数
这里应该就是处理引用计数相关的工作,所以对象的结构都需要。
struct _block_byref_foo {
void *isa;
struct _block_byref_foo *forwarding;
int flags; //refcount;
int size;
// helper functions called via Block_copy() and Block_release()
void (*byref_keep)(void *dst, void *src);
void (*byref_dispose)(void *);
typeof(marked_variable) marked_variable;
};
这个结构体这样初始化的:
forwarding
被设置为这个闭包结构的开始size
字段初始化为闭包结构的总大小
。- 如果不需要辅助函数,则
flags
字段设置为0
,如果需要,则设置为(1<<25)
。 - 辅助函数被初始化(
如果存在
)。 变量本身
被设置为其初始值
。isa
字段被设置为NULL
。
- 为什么需要设置
forwarding
指向自己呢?
因为block
创建时都是在栈上,当block
被copy
到堆上时,就会存在两份自己,一份位于栈
,一份位于堆
,forwarding
在这里就会指向堆
的那个自己,而不会指向随时可能释放的栈
的自己。
访问__block的变量(声明范围内)
前面说了 __block
修饰的变量会被编译器转义为_block_byref_<name>
,那么当我们访问它时,又有什么不同呢? 看下面这段代码
int __block i = 10;
i = 11;
会被编译为:
struct _block_byref_i {
void *isa;
struct _block_byref_i *forwarding;
int flags; //refcount;
int size;
int captured_i;
} i = { NULL, &i, 0, sizeof(struct _block_byref_i), 10 };
i.forwarding->captured_i = 11;
那么问题又来了,对于需要引用计数的对象呢?对的,是应该就需要helper functions
了
如下代码:
__block void (voidBlock)(void) = blockA;
voidBlock = blockB;
会被转化为:
struct _block_byref_voidBlock {
void *isa;
struct _block_byref_voidBlock *forwarding;
int flags; //refcount;
int size;
void (*byref_keep)(struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src);
void (*byref_dispose)(struct _block_byref_voidBlock *);
void (^captured_voidBlock)(void);
};
void _block_byref_keep_helper(struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src) {
//_Block_copy_assign(&dst->captured_voidBlock, src->captured_voidBlock, 0);
_Block_object_assign(&dst->captured_voidBlock, src->captured_voidBlock, BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER);
}
void _block_byref_dispose_helper(struct _block_byref_voidBlock *param) {
//_Block_destroy(param->captured_voidBlock, 0);
_Block_object_dispose(param->captured_voidBlock, BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER)}
然后
struct _block_byref_voidBlock voidBlock = {( .forwarding=&voidBlock, .flags=(1<<25), .size=sizeof(struct _block_byref_voidBlock *),
.byref_keep=_block_byref_keep_helper, .byref_dispose=_block_byref_dispose_helper,
.captured_voidBlock=blockA )};
voidBlock.forwarding->captured_voidBlock = blockB;
访问__block的变量(block内)
我们知道 __block
会直接拷贝指针引用
,所以 __block
声明的变量都必须实现 helper fuctions
以便于运行时进行拷贝
和释放
指针,利用_Block_object_assign
和 _Block_object_dispose
。
例如:
int __block i = 2;
functioncall(^{ i = 10; });
会被编译器转化为:
struct _block_byref_i {
void *isa; // set to NULL
struct _block_byref_voidBlock *forwarding;
int flags; //refcount;
int size;
void (*byref_keep)(struct _block_byref_i *dst, struct _block_byref_i *src);
void (*byref_dispose)(struct _block_byref_i *);
int captured_i;
};
struct __block_literal_5 {
void *isa;
int flags;
int reserved;
void (*invoke)(struct __block_literal_5 *);
struct __block_descriptor_5 *descriptor;
struct _block_byref_i *i_holder;
};
void __block_invoke_5(struct __block_literal_5 *_block) {
_block->forwarding->captured_i = 10;
}
void __block_copy_5(struct __block_literal_5 *dst, struct __block_literal_5 *src) {
//_Block_byref_assign_copy(&dst->captured_i, src->captured_i);
_Block_object_assign(&dst->captured_i, src->captured_i, BLOCK_FIELD_IS_BYREF | BLOCK_BYREF_CALLER);
}
void __block_dispose_5(struct __block_literal_5 *src) {
//_Block_byref_release(src->captured_i);
_Block_object_dispose(src->captured_i, BLOCK_FIELD_IS_BYREF | BLOCK_BYREF_CALLER);
}
static struct __block_descriptor_5 {
unsigned long int reserved;
unsigned long int Block_size;
void (*copy_helper)(struct __block_literal_5 *dst, struct __block_literal_5 *src);
void (*dispose_helper)(struct __block_literal_5 *);
} __block_descriptor_5 = { 0, sizeof(struct __block_literal_5) __block_copy_5, __block_dispose_5 };
然后block
初始化为:
struct _block_byref_i i = {( .isa=NULL, .forwarding=&i, .flags=0, .size=sizeof(struct _block_byref_i), .captured_i=2 )};
struct __block_literal_5 _block_literal = {
&_NSConcreteStackBlock,
(1<<25)|(1<<29), <uninitialized>,
__block_invoke_5,
&__block_descriptor_5,
&i,
};
引入的__attribute__((NSObject)) __block 变量
__attribute__((NSObject)) __block
修饰的变量,应该也具有 helper function
,和 __block
修饰的变量一样。
block 的释放
因为引用了_block变量
的block
可能会执行Block_copy()
,所以这些变量的底层存储
可能会移动到堆
中。在Objective-C
垃圾收集中,只有编译环境中使用的堆
才是垃圾收集
,不需要进一步的操作。否则,编译器必须发出一个调用,以便在所有的转义
或终止作用域时
释放任意的堆
存储。调用应该是:
_Block_object_dispose(&_block_byref_foo, BLOCK_FIELD_IS_BYREF);
block 嵌套
block A
中可以包含 block B
,所以按之前所说,block A
结构中可能包含一个成员 _block_literal (为B)
,所有B
中包含的常量
和变量
都将会被拷贝到block A
即使没有被使用。这也包括const常量
以及block块
Object-C扩展
block中引入一个NSObject对象
对象应该被看作是_attribute__((NSObject))
变量;所有的copy_helper、dispose_helper、byref_keep
和byref_dispose
帮助函数都应该使用_Block_object_assign
和_Block_object_dispose
。不应该生成使用*-retain
或*-release
方法的代码。
block当作为一个对象
编译器在合成属性setter
和getter
时将block
视为对象
,和对象一样能够进行强引用和弱引用
__weak __block支持
Objective-C
(和objective - c++
)支持在block
变量上的_weak
属性。通常情况下,编译器会使用Objective-C运行时
的helper fucntion
调用objc_assign_weak
和objc_read_weak
。这两种方法都应该用于对所有__weak __block
变量的读写:
对于常量 i
objc_read_weak(&block->byref_i->forwarding->i)
_weak
变量存储在_block_byref_foo
结构中,Block
有copy
和dispose
的helper
函数,helper 函数的调用:
_Block_object_assign(&dest->_block_byref_i, src-> _block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BYREF);
and
_Block_object_dispose(src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BYREF);
对于对象
和常量的区别除了拷贝指针外,在于flag
不同,使用 BLOCK_FIELD_IS_OBJECT
_Block_object_assign(&dest->_block_byref_i, src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_OBJECT | BLOCK_BYREF_CALLER);
例如:
__block __weak id obj = <initialization expression>;
functioncall(^{ [obj somemessage]; });
会被转化为:
void *isa; // uninitialized
struct _block_byref_obj *forwarding;
int flags; //refcount;
int size;
void (*byref_keep)(struct _block_byref_i *dst, struct _block_byref_i *src);
void (*byref_dispose)(struct _block_byref_i *);
id captured_obj;
};
void _block_byref_obj_keep(struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src) {
//_Block_copy_assign(&dst->captured_obj, src->captured_obj, 0);
_Block_object_assign(&dst->captured_obj, src->captured_obj, BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK | BLOCK_BYREF_CALLER);
}
void _block_byref_obj_dispose(struct _block_byref_voidBlock *param) {
//_Block_destroy(param->captured_obj, 0);
_Block_object_dispose(param->captured_obj, BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK | BLOCK_BYREF_CALLER);
};
__weak __block
修饰的则是flag
的不同。
对于block
使用 BLOCK_FIELD_IS_BLOCK
_Block_object_assign(&dest->_block_byref_i, src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER);
struct __block_literal_5 {
void *isa;
int flags;
int reserved;
void (*invoke)(struct __block_literal_5 *);
struct __block_descriptor_5 *descriptor;
struct _block_byref_obj *byref_obj;
};
void __block_invoke_5(struct __block_literal_5 *_block) {
[objc_read_weak(&_block->byref_obj->forwarding->captured_obj) somemessage];
}
void __block_copy_5(struct __block_literal_5 *dst, struct __block_literal_5 *src) {
//_Block_byref_assign_copy(&dst->byref_obj, src->byref_obj);
_Block_object_assign(&dst->byref_obj, src->byref_obj, BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK);
}
void __block_dispose_5(struct __block_literal_5 *src) {
//_Block_byref_release(src->byref_obj);
_Block_object_dispose(src->byref_obj, BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK);
}
static struct __block_descriptor_5 {
unsigned long int reserved;
unsigned long int Block_size;
void (*copy_helper)(struct __block_literal_5 *dst, struct __block_literal_5 *src);
void (*dispose_helper)(struct __block_literal_5 *);
} __block_descriptor_5 = { 0, sizeof(struct __block_literal_5), __block_copy_5, __block_dispose_5 };
对象
和block
两者初始化都为
truct _block_byref_obj obj = {( .forwarding=&obj, .flags=(1<<25), .size=sizeof(struct _block_byref_obj),
.byref_keep=_block_byref_obj_keep, .byref_dispose=_block_byref_obj_dispose,
.captured_obj = <initialization expression> )};
truct __block_literal_5 _block_literal = {
&_NSConcreteStackBlock,
(1<<25)|(1<<29), <uninitialized>,
__block_invoke_5,
&__block_descriptor_5,
&obj, // a reference to the on-stack structure containing "captured_obj"
};
functioncall(_block_literal->invoke(&_block_literal));
C++支持
拷贝内容
当一个C++
对象存在于block
块中时,flag
则会标记为(1<<26)
,当然同时还有(1<<25)
{
FOO foo;
void (^block)(void) = ^{ printf("%d\n", foo.value()); };
}
编译器会生成如下:
struct __block_literal_10 {
void *isa;
int flags;
int reserved;
void (*invoke)(struct __block_literal_10 *);
struct __block_descriptor_10 *descriptor;
const FOO foo;
};
void __block_invoke_10(struct __block_literal_10 *_block) {
printf("%d\n", _block->foo.value());
}
void __block_literal_10(struct __block_literal_10 *dst, struct __block_literal_10 *src) {
FOO_ctor(&dst->foo, &src->foo);
}
void __block_dispose_10(struct __block_literal_10 *src) {
FOO_dtor(&src->foo);
}
static struct __block_descriptor_10 {
unsigned long int reserved;
unsigned long int Block_size;
void (*copy_helper)(struct __block_literal_10 *dst, struct __block_literal_10 *src);
void (*dispose_helper)(struct __block_literal_10 *);
} __block_descriptor_10 = { 0, sizeof(struct __block_literal_10), __block_copy_10, __block_dispose_10 };
然后构建为:
{
FOO foo;
comp_ctor(&foo); // default constructor
struct __block_literal_10 _block_literal = {
&_NSConcreteStackBlock,
(1<<25)|(1<<26)|(1<<29), <uninitialized>,
__block_invoke_10,
&__block_descriptor_10,
};
comp_ctor(&_block_literal->foo, &foo); // const copy into stack version
struct __block_literal_10 &block = &_block_literal; // assign literal to block variable
block->invoke(block); // invoke block
comp_dtor(&_block_literal->foo); // destroy stack version of const block copy
comp_dtor(&foo); // destroy original version
}
拷贝指针
如果添加了__block
前缀
__block FOO blockStorageFoo;
那么构建时就需要
FOO_ctor(& _block_byref_blockStorageFoo->blockStorageFoo);
析构:
FOO_dtor(& _block_byref_blockStorageFoo->blockStorageFoo);
这里指的注意的是并没有使用
forwarding
指针
编译器生成的helper fuctions
如下:
void _block_byref_obj_keep(struct _block_byref_blockStorageFoo *dst, struct _block_byref_blockStorageFoo *src) {
FOO_ctor(&dst->blockStorageFoo, &src->blockStorageFoo);
}
void _block_byref_obj_dispose(struct _block_byref_blockStorageFoo *src) {
FOO_dtor(&src->blockStorageFoo);
}
为了支持成员变量和函数访问,编译器将合成一个指向该指针
的block 版本
的const指针
。
Runtime Helper Functions
runtime helper functions
可以在 /usr/local/include/Block_private.h.
查看描述。
helper fuctions
应该对每个block
调用
_Block_object_assign(&dst->target, src->target, BLOCK_FIELD_<apropos>);
和
_Block_object_dispose(->target, BLOCK_FIELD_<apropos>);
值得注意的是编译器包裹这两个函数的函数命名是__block_copy_i
和__block_dispose_
,
__block_copy_
在block
被copy
到堆上时被调用__block_dispose_
在堆上的block
被释放时调用
block
何时会触发copy
呢?
- 调用
Block
的copy
实例方法 Block
作为函数返回值返回时- 将
Block
赋值给附有__strong
修饰符id类型的类或Block
类型成员变量时 - 在方法名中含有
usingBlock
的Cocoa框架
方法或Grand Central Dispatch
的API
中传递Block
时
apropos
的枚举如下:
enum {
BLOCK_FIELD_IS_OBJECT = 3, // id, NSObject, __attribute__((NSObject)), block, ...
BLOCK_FIELD_IS_BLOCK = 7, // a block variable
BLOCK_FIELD_IS_BYREF = 8, // the on stack structure holding the __block variable
BLOCK_FIELD_IS_WEAK = 16, // declared __weak
BLOCK_BYREF_CALLER = 128, // called from byref copy/dispose helpers
};
其含义和使用上面的例子已经用到了
BLOCK_FIELD_IS_OBJECT
表示对象
BLOCK_FIELD_IS_BLOCK
表示一个block对象
BLOCK_FIELD_IS_BYREF
表示指针引用,意味着拷贝指针
BLOCK_FIELD_IS_WEAK
表示__weak属性
BLOCK_BYREF_CALLER
表示使用helper functions
实现copy/dispose
方法,兼容runtime
附上源代码中的代码摘要:
/* Certain field types require runtime assistance when being copied to the
heap. The following function is used to copy fields of types: blocks,
pointers to byref structures, and objects (including
__attribute__((NSObject)) pointers. BLOCK_FIELD_IS_WEAK is orthogonal to
the other choices which are mutually exclusive. Only in a Block copy
helper will one see BLOCK_FIELD_IS_BYREF.
*/
void _Block_object_assign(void *destAddr, const void *object, const int flags);
/* Similarly a compiler generated dispose helper needs to call back for each
field of the byref data structure. (Currently the implementation only
packs one field into the byref structure but in principle there could be
more). The same flags used in the copy helper should be used for each
call generated to this function:
*/
void _Block_object_dispose(const void *object, const int flags);
这篇文章已经了解了block
的实现和参数捕获,那么block
带参数时,编译器是如何构建的呢?再者,我们如何创建一个参数个数不确定时,如何创建block
?
带形参的block
main.m
我们通过反编译
#import <stdio.h>
int main () {
void (^block)(int value, char* str) = ^(int value, char* str) {
printf("value : %d str : %s",value,str);
};
block(2,"char test");
return 0;
}
clang -rewrite-objc main.m -o test.cpp
得到
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int value, char *str) {
printf("value : %d str : %s",value,str);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main () {
void (*block)(int value, char* str) = ((void (*)(int, char *))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *, int, char *))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 2, "char test");
return 0;
}
即block
参数,由定义的func
决定,那么等于就是block
映射了一个c函数
,这个函数
的形参和block
的形参个数一致。
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int value, char *str) {
printf("value : %d str : %s",value,str);
}