block类型
我们都知道block有三种类型:NSGlobalBlock、NSStackBlock、NSMallocBlock,一个block没有引用外部变量的那它就是NSGlobalBlock,如果引用了外部变量,它是先声明成NSStackBlock然后经过block_copy方法变成了NSMallocBlock。在我们开发过程中如果直接打印某个block对象,是无法看到其类型的。但通过lldb调试后在汇编层是可以查看具体类型变化的(注意要用真机)。有如下代码:
__block NSString *bb = [NSString stringWithFormat:@"mm"];
void(^blockbb)(void) = ^{
NSLog(@"%@",bb);
};
blockbb();
断点后查看汇编代码:
step1.png
然后来到
step2.png
按住Ctrol+Step Into,此时读取寄存器
lldb) register read x0
x0 = 0x000000016f07f6f8
(lldb) po 0x000000016f07f6f8
<__NSStackBlock__: 0x16f07f6f8>
signature: "v8@?0"
invoke : 0x100d86320 (/private/var/containers/Bundle/Application/2400E0E1-2CBA-42B3-B540-F104929D5B03/004-Block结构与签名.app/004-Block结构与签名`__main_block_invoke.10)
copy : 0x100d862b0 (/private/var/containers/Bundle/Application/2400E0E1-2CBA-42B3-B540-F104929D5B03/004-Block结构与签名.app/004-Block结构与签名`__copy_helper_block_e8_32r)
dispose : 0x100d862c0 (/private/var/containers/Bundle/Application/2400E0E1-2CBA-42B3-B540-F104929D5B03/004-Block结构与签名.app/004-Block结构与签名`__destroy_helper_block_e8_32r)
继续往下执行,直到来到_Block_copy的汇编代码
step3.png
在最下面的ret处打个断点,再次读取寄存器x0
(lldb) register read x0
x0 = 0x00000002820d0000
(lldb) po 0x00000002820d0000
<__NSMallocBlock__: 0x2820d0000>
signature: "v8@?0"
invoke : 0x100d86320 (/private/var/containers/Bundle/Application/2400E0E1-2CBA-42B3-B540-F104929D5B03/004-Block结构与签名.app/004-Block结构与签名`__main_block_invoke.10)
copy : 0x100d862b0 (/private/var/containers/Bundle/Application/2400E0E1-2CBA-42B3-B540-F104929D5B03/004-Block结构与签名.app/004-Block结构与签名`__copy_helper_block_e8_32r)
dispose : 0x100d862c0 (/private/var/containers/Bundle/Application/2400E0E1-2CBA-42B3-B540-F104929D5B03/004-Block结构与签名.app/004-Block结构与签名`__destroy_helper_block_e8_32r)
clang分析
在main.m中有如下代码:
void(^block1)(void) = ^{
printf("global block");
};
block1();
int a = 10;
void(^block2)(void) = ^{
printf("stack block %d",a);
};
block2();
__block int b = 10;
void(^block3)(void) = ^{
printf("stack block %d",b++);
};
block3();
NSString *aa = @"aa";
void(^blockaa)(void) = ^{
NSLog(@"%@",aa);
};
blockaa();
__block NSString *bb = [NSString stringWithFormat:@"mm"];
void(^blockbb)(void) = ^{
bb = [bb stringByAppendingString:@"+++"];
NSLog(@"%@",bb);
};
blockbb();
我们利用clang编译器生成.cpp文件后,查看一下block的底层实现,命令:
clang -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.2.sdk main.m
先看一下block1的生成代码:
void(*block1)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)block1)->FuncPtr)((__block_impl *)block1);
//block1对应的结构体
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;
}
};
//block1对应的方法实现
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("global block");
}
//block1对应的block描述
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)};
block2的生成代码,注意int a未使用__block:
int a = 10;
//此处是把a的值直接传进去了
void(*block2)(void) = ((void (*)())&__main_block_impl_1((void *)__main_block_func_1, &__main_block_desc_1_DATA, a));
((void (*)(__block_impl *))((__block_impl *)block2)->FuncPtr)((__block_impl *)block2);
//block2的结构体只比block1多一个int a,其他不变
struct __main_block_impl_1 {
struct __block_impl impl;
struct __main_block_desc_1* Desc;
int a;
__main_block_impl_1(void *fp, struct __main_block_desc_1 *desc, int _a, int flags=0) : a(_a) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_1(struct __main_block_impl_1 *__cself) {
//基础类型,是值传递
int a = __cself->a; // bound by copy
printf("stack block %d",a);
}
static struct __main_block_desc_1 {
size_t reserved;
size_t Block_size;
} __main_block_desc_1_DATA = { 0, sizeof(struct __main_block_impl_1)};
看一下block3的,int b使用了__block,所以整个变量b被包装成了__Block_byref类型的对象,拿到的b的指针地址,方便后续对原始的b进行操作:
__attribute__((__blocks__(byref))) __Block_byref_b_0 b = {(void*)0,(__Block_byref_b_0 *)&b, 0, sizeof(__Block_byref_b_0), 10};
void(*block3)(void) = ((void (*)())&__main_block_impl_2((void *)__main_block_func_2, &__main_block_desc_2_DATA, (__Block_byref_b_0 *)&b, 570425344));
((void (*)(__block_impl *))((__block_impl *)block3)->FuncPtr)((__block_impl *)block3);
//__block声明的int b会被包装成block_byref类型的对象
struct __Block_byref_b_0 {
void *__isa;
__Block_byref_b_0 *__forwarding;//直接指向外面定义的int b包装对象
int __flags;
int __size;
int b;//block内部他用的b
};
struct __main_block_impl_2 {
struct __block_impl impl;
struct __main_block_desc_2* Desc;
__Block_byref_b_0 *b; // by ref
__main_block_impl_2(void *fp, struct __main_block_desc_2 *desc, __Block_byref_b_0 *_b, int flags=0) : b(_b->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_2(struct __main_block_impl_2 *__cself) {
__Block_byref_b_0 *b = __cself->b; // bound by ref
//所以此处操作__forwarding->b,就是操作的外部的包装对象
printf("stack block %d",(b->__forwarding->b)++);
}
static void __main_block_copy_2(struct __main_block_impl_2*dst, struct __main_block_impl_2*src) {_Block_object_assign((void*)&dst->b, (void*)src->b, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_2(struct __main_block_impl_2*src) {_Block_object_dispose((void*)src->b, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __main_block_desc_2 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_2*, struct __main_block_impl_2*);
void (*dispose)(struct __main_block_impl_2*);
} __main_block_desc_2_DATA = { 0, sizeof(struct __main_block_impl_2), __main_block_copy_2, __main_block_dispose_2};
blockaa的,NSString *aa和上面的int a没啥区别,都没对变量进行再次包装,在block中打印aa时也是直接拿的外部传进来的aa的指针进行的:
NSString *aa = (NSString *)&__NSConstantStringImpl__var_folders_np_137s52wn33b_l8qp8rpx951m0000gq_T_main_e269d6_mi_0;
void(*blockaa)(void) = ((void (*)())&__main_block_impl_3((void *)__main_block_func_3, &__main_block_desc_3_DATA, aa, 570425344));
((void (*)(__block_impl *))((__block_impl *)blockaa)->FuncPtr)((__block_impl *)blockaa);
struct __main_block_impl_3 {
struct __block_impl impl;
struct __main_block_desc_3* Desc;
NSString *aa;
__main_block_impl_3(void *fp, struct __main_block_desc_3 *desc, NSString *_aa, int flags=0) : aa(_aa) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_3(struct __main_block_impl_3 *__cself) {
NSString *aa = __cself->aa; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_np_137s52wn33b_l8qp8rpx951m0000gq_T_main_e269d6_mi_1,aa);
}
static void __main_block_copy_3(struct __main_block_impl_3*dst, struct __main_block_impl_3*src) {_Block_object_assign((void*)&dst->aa, (void*)src->aa, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_3(struct __main_block_impl_3*src) {_Block_object_dispose((void*)src->aa, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static struct __main_block_desc_3 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_3*, struct __main_block_impl_3*);
void (*dispose)(struct __main_block_impl_3*);
} __main_block_desc_3_DATA = { 0, sizeof(struct __main_block_impl_3), __main_block_copy_3, __main_block_dispose_3};
最复杂的blockbb的,这里对NSString *bb变量的包装,比上面的int b还多了一层__Block_byref_id_object_copy和__Block_byref_id_object_dispose,这是因为对象类型不仅需要传值,需要考虑引用计数增加以及释放:
__attribute__((__blocks__(byref))) __Block_byref_bb_1 bb = {(void*)0,(__Block_byref_bb_1 *)&bb, 33554432, sizeof(__Block_byref_bb_1), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSString * _Nonnull (*)(id, SEL, NSString * _Nonnull, ...))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl__var_folders_np_137s52wn33b_l8qp8rpx951m0000gq_T_main_e269d6_mi_2)};
void(*blockbb)(void) = ((void (*)())&__main_block_impl_4((void *)__main_block_func_4, &__main_block_desc_4_DATA, (__Block_byref_bb_1 *)&bb, 570425344));
((void (*)(__block_impl *))((__block_impl *)blockbb)->FuncPtr)((__block_impl *)blockbb);
struct __Block_byref_bb_1 {
void *__isa;
__Block_byref_bb_1 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
NSString *bb;
};
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
static void __Block_byref_id_object_dispose_131(void *src) {
_Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}
struct __main_block_impl_4 {
struct __block_impl impl;
struct __main_block_desc_4* Desc;
__Block_byref_bb_1 *bb; // by ref
__main_block_impl_4(void *fp, struct __main_block_desc_4 *desc, __Block_byref_bb_1 *_bb, int flags=0) : bb(_bb->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_4(struct __main_block_impl_4 *__cself) {
__Block_byref_bb_1 *bb = __cself->bb; // bound by ref
(bb->__forwarding->bb) = ((NSString *(*)(id, SEL, NSString * _Nonnull))(void *)objc_msgSend)((id)(bb->__forwarding->bb), sel_registerName("stringByAppendingString:"), (NSString *)&__NSConstantStringImpl__var_folders_np_137s52wn33b_l8qp8rpx951m0000gq_T_main_6649bc_mi_3);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_np_137s52wn33b_l8qp8rpx951m0000gq_T_main_6649bc_mi_4,(bb->__forwarding->bb));
}
static void __main_block_copy_4(struct __main_block_impl_4*dst, struct __main_block_impl_4*src) {_Block_object_assign((void*)&dst->bb, (void*)src->bb, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_4(struct __main_block_impl_4*src) {_Block_object_dispose((void*)src->bb, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __main_block_desc_4 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_4*, struct __main_block_impl_4*);
void (*dispose)(struct __main_block_impl_4*);
} __main_block_desc_4_DATA = { 0, sizeof(struct __main_block_impl_4), __main_block_copy_4, __main_block_dispose_4};
源码解读
上面我们从编译器层面看一下block的实现原理,但block其实还有它的底层实现,例如_Block_copy、_Block_object_assign、_Block_object_dispose的具体实现是怎样的呢。可以通过查看block的底层libclosure-73源码来分析一下
_Block_copy源码
block对象在底层实现的关键方法
void *_Block_copy(const void *arg) {
struct Block_layout *aBlock;
if (!arg) return NULL;
// The following would be better done as a switch statement
aBlock = (struct Block_layout *)arg;
if (aBlock->flags & BLOCK_NEEDS_FREE) {
// latches on high
latching_incr_int(&aBlock->flags);
return aBlock;
}
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
return aBlock; // gloal类型的,啥都不需要做
}
else { // statck类型的
// Its a stack block. Make a copy.
struct Block_layout *result =
(struct Block_layout *)malloc(aBlock->descriptor->size);
if (!result) return NULL;
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
#if __has_feature(ptrauth_calls)
// Resign the invoke pointer as it uses address authentication.
result->invoke = aBlock->invoke;
#endif
// reset refcount
result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING); // XXX not needed
result->flags |= BLOCK_NEEDS_FREE | 2; // logical refcount 1
//对于需要持有外部变量的都会触发block对象的__main_block_copy方法
_Block_call_copy_helper(result, aBlock);
// Set isa last so memory analysis tools see a fully-initialized object.
//转换成mallocBlock
result->isa = _NSConcreteMallocBlock;
return result;
}
}
_Block_object_assign源码
block对象捕获外部y变量的关键方法。对于需要持有外部变量的block,在转换成mallocBlock后都会调用其对应的__main_block_copy方法,而__main_block_copy方法就调用_Block_object_assign方法来对外部的包装对象进行赋值逻辑判断
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)) {
//未用__block修饰的外部变量,交给系统进行内存管理,如果是ARC的话会进行自动的retain和release,这也是为什么block会暂时持有外部对象的原因
case BLOCK_FIELD_IS_OBJECT:
_Block_retain_object(object);
*dest = object;
break;
//block类型的变量,再次走到上面的block_copy中来
case BLOCK_FIELD_IS_BLOCK:
*dest = _Block_copy(object);
break;
//__block修饰的变量,会进入到_Block_byref_copy
case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
case BLOCK_FIELD_IS_BYREF:
*dest = _Block_byref_copy(object);
break;
//以下几种类型都不会持有对象了
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_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:
*dest = object;
break;
default:
break;
}
}
_Block_byref_copy源码
外部变量包装对象的复制方法
static struct Block_byref *_Block_byref_copy(const void *arg) {
// Block_byref 结构体
struct Block_byref *src = (struct Block_byref *)arg;
if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
// src points to stack
struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
copy->isa = NULL;
// byref value 4 is logical refcount of 2: one for caller, one for stack
copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
// 对传进来的对象再次copy,并且修改了src的forwarding为新的对象
copy->forwarding = copy; // patch heap copy to point to itself
src->forwarding = copy; // patch stack to point to heap copy
copy->size = src->size;
if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
// Trust copy helper to copy everything of interest
// If more than one field shows up in a byref block this is wrong XXX
struct Block_byref_2 *src2 = (struct Block_byref_2 *)(src+1);
struct Block_byref_2 *copy2 = (struct Block_byref_2 *)(copy+1);
copy2->byref_keep = src2->byref_keep;
copy2->byref_destroy = src2->byref_destroy;
if (src->flags & BLOCK_BYREF_LAYOUT_EXTENDED) {
struct Block_byref_3 *src3 = (struct Block_byref_3 *)(src2+1);
struct Block_byref_3 *copy3 = (struct Block_byref_3*)(copy2+1);
copy3->layout = src3->layout;
}
//此处是触发外部Block_byref_id_object_copy方法调用的关键,如果__block修饰的对象类型的变量,还需要进一步执行_Block_object_assign
(*src2->byref_keep)(copy, src);
}
else {
// Bitwise copy.
// This copy includes Block_byref_3, if any.
memmove(copy+1, src+1, src->size - sizeof(*src));
}
}
// already copied to heap
else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
latching_incr_int(&src->forwarding->flags);
}
return src->forwarding;
}
网友评论