块实现规范¶
历史¶
2008/7/14 - 创建。
2008/8/21 - 修订,C++。
2008/9/24 - 添加
NULL
isa
字段到__block
存储。2008/10/1 - 修订块布局以使用
static
描述符结构。2008/10/6 - 修订块布局以使用无符号长整型标志。
2008/10/28 - 指定使用
_Block_object_assign
和_Block_object_dispose
用于帮助函数中的所有“对象”类型。2008/10/30 - 修订新布局以将调用函数置于同一位置。
2008/10/30 - 添加
__weak
支持。2010/3/16 - 修订用于 stret 返回、签名字段。
2010/4/6 - 改进了措辞。
2013/1/6 - 改进了措辞并转换为 rst。
本文档描述了 Apple ABI 块实现规范。
此 ABI 的第一个发布版本在 Mac OS X 10.6 中找到,将被称为 10.6.ABI。截至 2010/3/16,以下内容描述了与运行时和编译器之间的 ABI 契约,并且必要时,将被称为 ABI.2010.3.16。
由于 Apple ABI 引用了系统其他元素中的符号,因此在 SnowLeopard 之前系统上使用此 ABI 的任何尝试都是未定义的。
高级¶
Blocks
的 ABI 由其布局和编译器所需的运行时函数组成。类型为 R (^)(P...)
的 Block
包含以下形式的结构
struct Block_literal_1 {
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
int flags;
int reserved;
R (*invoke)(struct Block_literal_1 *, P...);
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
};
以下标志位用于可能的 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) 通常被设置,并且始终被运行时忽略 - 它一直是一个过渡标记,在过渡后没有被删除。现在此位与 (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,
}
签名字段并不总是填充。
以下讨论以 10.6.ABI 为准。
Block
文本可能出现在结构在堆栈局部内存中创建的函数中。它们也可能作为全局或 static
局部变量的 Block
变量的初始化表达式出现。
当 Block
文本表达式被求值时,基于堆栈的结构将按以下方式初始化
一个
static
描述符结构被声明并按以下方式初始化
a.
invoke
函数指针被设置为一个函数,该函数将Block
结构作为其第一个参数,以及Block
的其余参数(如果有),并执行Block
复合语句。b.
size
字段被设置为以下Block
文本结构的大小。c.
copy_helper
和dispose_helper
函数指针如果被Block
文本需要,将被设置为各自的帮助函数。
一个堆栈(或全局)
Block
文本数据结构被创建并按以下方式初始化a.
isa
字段被设置为外部_NSConcreteStackBlock
的地址,它是在libSystem
中提供的未初始化内存块,或者如果这是静态或文件级Block
文本,则为_NSConcreteGlobalBlock
。b.
flags
字段被设置为零,除非有变量被导入到Block
中,这些变量需要帮助函数才能执行程序级Block_copy()
和Block_release()
操作,在这种情况下,(1<<25) 标志位将被设置。
例如,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
};
一个 Block
导入其他 Block
引用、其他变量的 const
复制以及标记为 __block
的变量。在 Objective-C 中,变量还可以是对象。
当 Block
文本表达式用作全局或 static
局部变量的初始值时,它将按以下方式初始化
struct __block_literal_1 __block_literal_1 = {
&_NSConcreteGlobalBlock,
(1<<28)|(1<<29), <uninitialized>,
__block_invoke_1,
&__block_descriptor_1
};
也就是说,一个不同的地址作为第一个值提供,并且在 flags
字段中设置了一个特定的 (1<<28) 位,否则它与基于堆栈的 Block
文本相同。这是一种优化,可以用于任何不导入 const
或 __block
存储变量的 Block
文本。
导入的变量¶
auto
存储类变量被导入为 const
复制。 __block
存储类变量被导入为指向封闭数据结构的指针。全局变量只是被引用,而不是被视为导入的。
导入的 const
复制变量¶
未用 __block
标记的自动存储变量被导入为 const
复制。
最简单的例子是导入类型为 int
的变量
int x = 10;
void (^vv)(void) = ^{ printf("x is %d\n", x); }
x = 11;
vv();
它将被编译为
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) };
以及
struct __block_literal_2 __block_literal_2 = {
&_NSConcreteStackBlock,
(1<<29), <uninitialized>,
__block_invoke_2,
&__block_descriptor_2,
x
};
总之,标量、结构、联合和函数指针通常被导入为 const
复制,不需要帮助函数。
导入的 const
复制的 Block
引用¶
第一个需要复制和释放帮助函数的案例是,当 Block
本身被导入时。在这种情况下,需要 copy_helper
函数和 dispose_helper
函数。 copy_helper
函数同时传入现有的基于堆栈的指针和指向新的堆版本的指针,并且应该回调到运行时以实际执行对 Block
中导入的字段的复制操作。运行时函数都在 运行时帮助函数 中描述。
一个简单的例子
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,
};
以及使用该 Block
的地方
struct __block_literal_4 _block_literal = {
&_NSConcreteStackBlock,
(1<<25)|(1<<29), <uninitialized>
__block_invoke_4,
& __block_descriptor_4
existingBlock,
};
导入 __attribute__((NSObject))
变量¶
GCC 在结构指针上引入了 __attribute__((NSObject))
,表示“这是个对象”。这很有用,因为许多底层数据结构被声明为不透明结构指针,例如 CFStringRef
、CFArrayRef
等等。但是,从 C 使用时,它们仍然是真正的对象,这是需要生成复制和释放帮助函数的第二个案例。编译器生成的复制帮助函数应该使用 _Block_object_assign
运行时帮助函数,而在释放帮助函数中,应该调用 _Block_object_dispose
运行时帮助函数。
例如,以下代码中的 Block
foo
struct Opaque *__attribute__((NSObject)) objectPointer = ...;
...
void (^foo)(void) = ^{ CFPrint(objectPointer); };
将生成以下帮助函数
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
的变量嵌入到以下形式的专用结构中
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()
时,某些类型的变量需要辅助函数。在“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;
};
该结构被初始化,以便
a. 将
forwarding
指针设置为其封闭结构的开头。b. 将
size
字段初始化为封闭结构的总大小。c. 将
flags
字段设置为 0(如果不需要辅助函数)或 (1<<25)(如果需要)。
初始化辅助函数(如果存在)。
变量本身被设置为其初始值。
将
isa
字段设置为NULL
。
从其词法范围内访问 __block
变量¶
为了在 copy_helper
操作中将变量“移动”到堆中,编译器必须重写对该变量的访问,使其通过结构的 forwarding
指针进行间接访问。例如
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;
如果将 Block
引用变量标记为 __block
,则生成的辅助代码必须使用运行时提供的 _Block_object_assign
和 _Block_object_dispose
例程来进行复制。例如
__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
变量导入 Blocks
¶
在复合语句体中使用 __block
变量的 Block
必须导入该变量并发出 copy_helper
和 dispose_helper
辅助函数,这些函数反过来调用运行时来使用 _Block_object_assign
和 _Block_object_dispose
函数实际复制或释放 byref
数据块。
例如
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 };
以及
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
变量应具有 byref_keep
和 byref_dispose
辅助函数,这些函数使用 _Block_object_assign
和 _Block_object_dispose
。
__block
转义¶
因为引用 __block
变量的 Blocks
可能会执行 Block_copy()
,所以变量的基础存储可能会移动到堆中。在仅进行 Objective-C 垃圾回收的编译环境中,使用的堆是垃圾回收的堆,不需要采取进一步的操作。否则,编译器必须在所有转义或终止其范围时发出一个调用来释放 __block
变量的任何堆存储。调用应为
_Block_object_dispose(&_block_byref_foo, BLOCK_FIELD_IS_BYREF);
嵌套¶
Blocks
可能包含 Block
文字表达式。内部块中使用的任何变量都将导入到所有封闭的 Block
范围内,即使这些变量未被使用。这包括 const
导入以及 __block
变量。
Objective C 对 Blocks
的扩展¶
导入对象¶
应将对象视为 __attribute__((NSObject))
变量;所有 copy_helper
、dispose_helper
、byref_keep
和 byref_dispose
辅助函数应使用 _Block_object_assign
和 _Block_object_dispose
。不应该生成使用 *-retain
或 *-release
方法的代码。
Blocks
作为对象¶
编译器在合成属性设置器和获取器时会将 Blocks
视为对象,在生成垃圾回收强弱布局信息时会将它们描述为对象,以与对象相同的方式,并且会以与对象相同的方式发出强弱写屏障分配。
__weak __block
支持¶
Objective-C(和 Objective-C++)支持 __weak
属性在 __block
变量上。在正常情况下,编译器使用 Objective-C 运行时辅助支持函数 objc_assign_weak
和 objc_read_weak
。这两个函数都应该继续用于所有 __weak __block
变量的读写操作
objc_read_weak(&block->byref_i->forwarding->i)
将 __weak
变量存储在 _block_byref_foo
结构中,并且 Block
为该结构具有复制和释放辅助函数,这些函数调用
_Block_object_assign(&dest->_block_byref_i, src-> _block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BYREF);
以及
_Block_object_dispose(src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BYREF);
反过来,block_byref
复制支持辅助函数会区分 __block
变量是否为 Block
,并且应调用
_Block_object_assign(&dest->_block_byref_i, src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_OBJECT | BLOCK_BYREF_CALLER);
用于声明为对象的内容,或
_Block_object_assign(&dest->_block_byref_i, src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER);
用于声明为 Block
的内容。
以下是一个完整的示例
__block __weak id obj = <initialization expression>;
functioncall(^{ [obj somemessage]; });
将转换为
struct _block_byref_obj {
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);
};
用于块 byref
部分,以及
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 };
以及在复合语句中
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++ 对象使用复制构造函数复制到 const
副本中。如果基于堆栈的 C++ 对象在块内使用但没有复制构造函数,则会发生错误。此外,块必须合成复制和销毁辅助例程才能支持 Block_copy()
操作,并且除了 (1<<25) 位之外,标志还用 (1<<26) 位标记。复制辅助例程应使用提供的基于堆栈的块源和基于堆的目的地中变量的适当偏移量调用构造函数,以便对所有 const
构造的副本进行构造,并且类似地应在销毁例程中调用析构函数。
例如,假设存在一个带有复制构造函数的 C++ 类 FOO
。在代码块内,声明了 FOO
对象的堆栈版本,并在 Block
文字表达式中使用
{
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_copy_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
存储中的 C++ 对象最初存储在堆栈上的 block_byref
数据结构中,就像其他变量一样。此类对象(如果不是 const
对象)必须支持常规复制构造函数。block_byref
数据结构将具有由编译器合成的复制和销毁辅助例程。复制辅助例程将创建代码来执行基于初始堆栈 block_byref
数据结构的复制构造函数,并将设置 (1<<26) 位,以及 (1<<25) 位。销毁辅助例程将创建代码来对提供的 block_byref
堆数据结构中存储的对象执行析构函数。例如,
__block FOO blockStorageFoo;
需要嵌入的 blockStorageFoo
对象的正常构造函数
FOO_ctor(& _block_byref_blockStorageFoo->blockStorageFoo);
以及在范围终止时执行析构函数
FOO_dtor(& _block_byref_blockStorageFoo->blockStorageFoo);
请注意,不会使用转发间接访问。
编译器需要生成(如果从块文字中使用)以下复制/释放辅助函数
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);
}
用于类/结构 FOO
的适当命名的构造函数和析构函数。
为了支持成员变量和函数访问,编译器将合成指向块版本的 this
指针的 const
指针。
运行时辅助函数¶
运行时辅助函数在 /usr/local/include/Block_private.h
中描述。总结一下它们的用途,一个 Block
如果它导入任何块变量,__block
存储变量,__attribute__((NSObject))
变量,或 C++ const
复制对象带有构造函数/析构函数,则需要复制/释放辅助函数。将设置 (1<<26) 位并生成函数。
块复制辅助函数应该为上述类型中的每个变量调用
_Block_object_assign(&dst->target, src->target, BLOCK_FIELD_<apropos>);
在复制辅助函数中,以及
_Block_object_dispose(->target, BLOCK_FIELD_<apropos>);
在释放辅助函数中,其中 <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
};
当然还有 const
复制 C++ 对象的构造函数/析构函数。
类似地,block_byref
数据结构也需要复制/释放辅助函数来处理块变量,__attribute__((NSObject))
变量,或 C++ const
复制对象带有构造函数/析构函数,并且同样 (1<<26) 位被设置,函数以相同的方式生成。
在 ObjC 中,我们允许 __weak
作为 __block
变量的属性,这会导致添加 BLOCK_FIELD_IS_WEAK
或在 BLOCK_FIELD_IS_BYREF
标志上,在 Block
复制辅助函数中复制 block_byref
结构时,以及在 block_byref
复制/释放辅助函数调用中添加在 BLOCK_FIELD_<apropos>
字段上。
辅助函数的原型和摘要如下:
/* 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);
版权¶
版权所有 2008-2010 Apple, Inc. 特此免费授予任何获得本软件副本和相关文档文件(“软件”)的人无限制地处理软件的权利,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售软件副本的权利,以及允许获得软件的人员这样做,但须符合以下条件:
上述版权声明和本许可声明应包含在所有副本或软件的实质部分中。
本软件按“原样”提供,不提供任何形式的明示或暗示担保,包括但不限于适销性、特定用途适用性和非侵权担保。在任何情况下,作者或版权持有人均不对因软件或使用或其他处理软件而导致的任何索赔、损害或其他责任承担责任,无论是在合同、侵权或其他情况下产生的。