Block_copy原理
当我们在代码中使用:
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int var = 10;
void (^block)(void) = Block_copy(^{
printf("var = %d,<%p>\n", var++, &var);
});
block();
}
return 0;
}
使用命令:clang -rewrite-objc main.m
// 和前一章节一样,只是在main中多了_Block_copy函数
struct __Block_byref_var_0 {
void *__isa;
__Block_byref_var_0 *__forwarding;
int __flags;
int __size;
int var;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_var_0 *var; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_var_0 *_var, int flags=0) : var(_var->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_var_0 *var = __cself->var; // bound by ref
printf("var = %d,<%p>\n", (var->__forwarding->var)++, &(var->__forwarding->var));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->var, (void*)src->var, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->var, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */
{ __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_var_0 var = {(void*)0,(__Block_byref_var_0 *)&var, 0, sizeof(__Block_byref_var_0), 10};
// 这里多了_Block_copy函数
void (*block)(void) = ((void (*)())_Block_copy((const void *)(((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_var_0 *)&var, 570425344)))));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
在c++代码中,多了_Block_copy()函数,而这个函数在runtime.c中(已开源)。
runtime.m
_Block_copy()函数
void *_Block_copy(const void *arg) {
// Block_layout结构体就是block对应的原型
struct Block_layout *aBlock;
// 参数为nil,直接return
if (!arg) return NULL;
// 把参数结构体指针赋给局部变量
aBlock = (struct Block_layout *)arg;
// 如果该Block已经在堆上,refcount+1,然后return
if (aBlock->flags & BLOCK_NEEDS_FREE) {
// latches on high
latching_incr_int(&aBlock->flags);
return aBlock;
}
// 如果该Block在Data区,直接return,
// 第一章我们讲到global类型的block使用copy类型不会改变
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
return aBlock;
}
else {
// 如果是栈区的block,先使用malloc在堆区分配内存空间
struct Block_layout *result = malloc(aBlock->descriptor->size);
// 分配失败,return
if (!result) return NULL;
// 把参数Block中成员按位复制到新开辟的堆里
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
// 计数复位
result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING); // XXX not needed
// 逻辑或0x01,refcount复位1
result->flags |= BLOCK_NEEDS_FREE | 2; // logical refcount 1
// 调用了另一个函数,等会我们来看
_Block_call_copy_helper(result, aBlock);
// Set isa last so memory analysis tools see a fully-initialized object.
// 把isa类型指向为堆类型Block
result->isa = _NSConcreteMallocBlock;
return result;
}
}
// 在_Block_copy()中调用了该函数
static void _Block_call_copy_helper(void *result, struct Block_layout *aBlock)
{
/**
这个Block_descriptor_2结构体就是文章开头c++代码中的__main_block_desc_0_DATA结构体,
开头__main_block_desc_0_DATA结构体中是不是实现了copy的函数?
对应的函数是:__main_block_copy_0()
*/
struct Block_descriptor_2 *desc = _Block_descriptor_2(aBlock);
if (!desc) return;
// 这里就调用了__main_block_desc_0_DATA结构体中成员函数copy()
(*desc->copy)(result, aBlock); // do fixup
}
现在捋一捋调用的顺序:
Block_copy() -> _Block_copy() -> _Block_call_copy_helper() -> *desc->copy()
在_Block_copy()函数中在堆区开辟一个内存空间,并把原来在栈区的Block按位逐个复制到堆区,最后还会调用定义Block描述结构体中的copy函数。
_Block_object_assign
回到文章开头,copy函数指向了__main_block_copy_0函数,在该函数中调用_Block_object_assign函数,我们继续看代码:
// _Block_object_assign((void*)&dst->var, (void*)src->var, 8/*BLOCK_FIELD_IS_BYREF*/);
// 这里destArg和object都是指向var,而var是一个结构体,第三个参数为:BLOCK_FIELD_IS_BYREF
void _Block_object_assign(void *destArg, const void *object, const int flags) {
const void **dest = (const void **)destArg;
switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
case BLOCK_FIELD_IS_OBJECT:
/*******
id object = ...;
[^{ object; } copy];
********/
// 如果源参数是一个对象,那么引用计数+1
_Block_retain_object(object);
// 把现在指向堆区的指针重新指向原来该数据的内存地址
*dest = object;
break;
case BLOCK_FIELD_IS_BLOCK:
/*******
void (^object)(void) = ...;
[^{ object; } copy];
********/
/**
如果源参数是一个Block,则调用_Block_copy
还是和前面一样:
1. 该源Block在栈区,则会被复制到堆区;
2. 该源Block在堆区,引用计数+1;
3. 该源Block在Data,不会发生任何改变
*/
*dest = _Block_copy(object);
break;
case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
case BLOCK_FIELD_IS_BYREF:
/*******
// copy the onstack __block container to the heap
// Note this __weak is old GC-weak/MRC-unretained.
// ARC-style __weak is handled by the copy helper directly.
__block ... x;
__weak __block ... x;
[^{ x; } copy];
********/
// 如果在栈区的变量被__block修饰,那么它将被复制到堆区
*dest = _Block_byref_copy(object);
break;
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
/*******
// copy the actual field held in the __block container
// Note this is MRC unretained __block only.
// ARC retained __block is handled by the copy helper directly.
__block id object;
__block void (^object)(void);
[^{ object; } copy];
********/
// MRC模式下,使用__block的修饰对象引用计数不会发生任何变化,这样可以避免循环引用
// 把现在指向堆区的指针重新指向原来该数据的内存地址
*dest = object;
break;
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK | BLOCK_FIELD_IS_WEAK:
/*******
// copy the actual field held in the __block container
// Note this __weak is old GC-weak/MRC-unretained.
// ARC-style __weak is handled by the copy helper directly.
__weak __block id object;
__weak __block void (^object)(void);
[^{ object; } copy];
********/
// Block中引用了用__weak修饰的对象和Block,
// 把现在指向堆区的指针重新指向原来该数据的内存地址
*dest = object;
break;
default:
break;
}
}