Block捕获变量:
局部变量:基本数据类型:捕获值;对象型:捕获值和所有权修饰符
静态局部变量:捕获地址
全局变量:不捕获
静态全局变量:不捕获
捕获局部变量:非对象、对象、__block
(非对象)、__block
(对象)。
捕获非对象:
void blockStudy()
{
dispatch_block_t myBlock;
{
int a = 0;
myBlock = ^() {
if (a > 0) {
}
};
}
}
// ==>
void blockStudy()
{
dispatch_block_t myBlock;
{
int a = 0;
myBlock = ((void (*)())&__blockStudy_block_impl_0((void *)__blockStudy_block_func_0, &__blockStudy_block_desc_0_DATA, a));
}
}
struct __blockStudy_block_impl_0 {
struct __block_impl impl;
struct __blockStudy_block_desc_0* Desc;
int a;
__blockStudy_block_impl_0(void *fp, struct __blockStudy_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static struct __blockStudy_block_desc_0 {
size_t reserved;
size_t Block_size;
} __blockStudy_block_desc_0_DATA = { 0, sizeof(struct __blockStudy_block_impl_0)};
捕获对象:
void blockStudy()
{
dispatch_block_t myBlock;
{
MyObject *myObject = [MyObject new];
myBlock = ^() {
myObject = [MyObject new];
};
}
}
// ==>
void blockStudy()
{
dispatch_block_t myBlock;
{
MyObject *myObject = ((MyObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("MyObject"), sel_registerName("new"));
myBlock = ((void (*)())&__blockStudy_block_impl_0((void *)__blockStudy_block_func_0, &__blockStudy_block_desc_0_DATA, myObject, 570425344));
}
}
struct __blockStudy_block_impl_0 {
struct __block_impl impl;
struct __blockStudy_block_desc_0* Desc;
MyObject *__strong myObject;
__blockStudy_block_impl_0(void *fp, struct __blockStudy_block_desc_0 *desc, MyObject *__strong _myObject, int flags=0) : myObject(_myObject) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static struct __blockStudy_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __blockStudy_block_impl_0*, struct __blockStudy_block_impl_0*);
void (*dispose)(struct __blockStudy_block_impl_0*);
} __blockStudy_block_desc_0_DATA = { 0, sizeof(struct __blockStudy_block_impl_0), __blockStudy_block_copy_0, __blockStudy_block_dispose_0};
static void __blockStudy_block_copy_0(struct __blockStudy_block_impl_0*dst, struct __blockStudy_block_impl_0*src) {_Block_object_assign((void*)&dst->myObject, (void*)src->myObject, 3/*BLOCK_FIELD_IS_OBJECT*/);}
可以发现__blockStudy_block_desc_0
相比捕获非对象的多了两个函数指针:copy
、dispose
。当block被拷贝到堆上面时copy函数会执行,调用_Block_object_assign
拷贝捕获的对象的地址并增加该对象的引用计数。
捕获__block(非对象)
void blockStudy()
{
dispatch_block_t myBlock;
{
__block int myInt = 0;
myBlock = ^() {
myInt = 1;
};
}
}
// ==>
void blockStudy()
{
dispatch_block_t myBlock;
{
__attribute__((__blocks__(byref))) __Block_byref_myInt_0 myInt = {(void*)0,(__Block_byref_myInt_0 *)&myInt, 0, sizeof(__Block_byref_myInt_0), 0};
myBlock = ((void (*)())&__blockStudy_block_impl_0((void *)__blockStudy_block_func_0, &__blockStudy_block_desc_0_DATA, (__Block_byref_myInt_0 *)&myInt, 570425344));
}
}
struct __Block_byref_myInt_0 {
void *__isa;
__Block_byref_myInt_0 *__forwarding;
int __flags;
int __size;
int myInt;
};
struct __blockStudy_block_impl_0 {
struct __block_impl impl;
struct __blockStudy_block_desc_0* Desc;
__Block_byref_myInt_0 *myInt; // by ref
__blockStudy_block_impl_0(void *fp, struct __blockStudy_block_desc_0 *desc, __Block_byref_myInt_0 *_myInt, int flags=0) : myInt(_myInt->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static struct __blockStudy_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __blockStudy_block_impl_0*, struct __blockStudy_block_impl_0*);
void (*dispose)(struct __blockStudy_block_impl_0*);
} __blockStudy_block_desc_0_DATA = { 0, sizeof(struct __blockStudy_block_impl_0), __blockStudy_block_copy_0, __blockStudy_block_dispose_0};
static void __blockStudy_block_copy_0(struct __blockStudy_block_impl_0*dst, struct __blockStudy_block_impl_0*src) {_Block_object_assign((void*)&dst->myInt, (void*)src->myInt, 8/*BLOCK_FIELD_IS_BYREF*/);}
这里和捕获对象自由变量的block相比同样也有copy函数指针,不过在调用_Block_object_assign
传的第三个参数不一样。
捕获__block(对象)
void blockStudy()
{
dispatch_block_t myBlock;
{
__block MyObject *myObject = [MyObject new];
myBlock = ^() {
myObject = [MyObject new];
};
}
}
// ==>
void blockStudy()
{
dispatch_block_t myBlock;
{
__attribute__((__blocks__(byref))) __Block_byref_myObject_0 myObject = {(void*)0,(__Block_byref_myObject_0 *)&myObject, 33554432, sizeof(__Block_byref_myObject_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((MyObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("MyObject"), sel_registerName("new"))};
myBlock = ((void (*)())&__blockStudy_block_impl_0((void *)__blockStudy_block_func_0, &__blockStudy_block_desc_0_DATA, (__Block_byref_myObject_0 *)&myObject, 570425344));
}
}
struct __Block_byref_myObject_0 {
void *__isa;
__Block_byref_myObject_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
MyObject *__strong myObject;
};
struct __blockStudy_block_impl_0 {
struct __block_impl impl;
struct __blockStudy_block_desc_0* Desc;
__Block_byref_myObject_0 *myObject; // by ref
__blockStudy_block_impl_0(void *fp, struct __blockStudy_block_desc_0 *desc, __Block_byref_myObject_0 *_myObject, int flags=0) : myObject(_myObject->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static struct __blockStudy_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __blockStudy_block_impl_0*, struct __blockStudy_block_impl_0*);
void (*dispose)(struct __blockStudy_block_impl_0*);
} __blockStudy_block_desc_0_DATA = { 0, sizeof(struct __blockStudy_block_impl_0), __blockStudy_block_copy_0, __blockStudy_block_dispose_0};
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 __blockStudy_block_copy_0(struct __blockStudy_block_impl_0*dst, struct __blockStudy_block_impl_0*src) {_Block_object_assign((void*)&dst->myObject, (void*)src->myObject, 8/*BLOCK_FIELD_IS_BYREF*/);}
这里的block同样有copy函数指针,但是__block变量
的结构体__Block_byref_myObject_0
也多了两个函数指针:__Block_byref_id_object_copy
和__Block_byref_id_object_dispose
。当block被拷贝到堆上面时,__block变量
的结构体__Block_byref_myObject_0
也会被拷贝到堆上面,这时__Block_byref_id_object_copy
就会被调用。
_Block_object_assign
看看_Block_object_assign
的源码:
/*
* When Blocks or Block_byrefs hold objects then their copy routine helpers use this entry point
* to do the assignment.
*/
void _Block_object_assign(void *destAddr, const void *object, const int flags) {
//printf("_Block_object_assign(*%p, %p, %x)\n", destAddr, object, flags);
if ((flags & BLOCK_BYREF_CALLER) == BLOCK_BYREF_CALLER) {
if ((flags & BLOCK_FIELD_IS_WEAK) == BLOCK_FIELD_IS_WEAK) {
_Block_assign_weak(object, destAddr);
}
else {
// do *not* retain or *copy* __block variables whatever they are
_Block_assign((void *)object, destAddr);
}
}
else if ((flags & BLOCK_FIELD_IS_BYREF) == BLOCK_FIELD_IS_BYREF) {
// copying a __block reference from the stack Block to the heap
// flags will indicate if it holds a __weak reference and needs a special isa
_Block_byref_assign_copy(destAddr, object, flags);
}
// (this test must be before next one)
else if ((flags & BLOCK_FIELD_IS_BLOCK) == BLOCK_FIELD_IS_BLOCK) {
// copying a Block declared variable from the stack Block to the heap
_Block_assign(_Block_copy_internal(object, flags), destAddr);
}
// (this test must be after previous one)
else if ((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT) {
//printf("retaining object at %p\n", object);
_Block_retain_object(object);
//printf("done retaining object at %p\n", object);
_Block_assign((void *)object, destAddr);
}
}
如果block捕获了对象自由变量,在拷贝的时候_Block_object_assign
传入的是BLOCK_FIELD_IS_OBJECT
,这时候会强引用这个对象,再拷贝地址。
如果block捕获了__block变量
,不管是非对象还是对象,在拷贝时_Block_object_assign
传入的是BLOCK_FIELD_IS_BYREF
,这时拷贝的是__block变量
,把__block变量
从栈拷贝到堆。
__block变量
的拷贝同样调用_Block_object_assign
,这时第三个参数为BLOCK_BYREF_CALLER|BLOCK_FIELD_IS_OBJECT
。
block对对象的持有是在_Block_object_assign
执行时完成的。
block会强引用__block结构体
,而__block结构体
强引用对象自由变量。
__weak修饰符
void blockStudy()
{
dispatch_block_t myBlock;
{
NSObject *myObject = [NSObject new];
__weak NSObject *weakMyObjetc = myObject;
myBlock = ^() {
NSLog(@"%@", weakMyObjetc);
};
}
}
// ==>
void blockStudy()
{
dispatch_block_t myBlock;
{
NSObject *myObject = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new"));
__attribute__((objc_ownership(weak))) NSObject *weakMyObjetc = myObject;
myBlock = ((void (*)())&__blockStudy_block_impl_0((void *)__blockStudy_block_func_0, &__blockStudy_block_desc_0_DATA, weakMyObjetc, 570425344));
}
}
struct __blockStudy_block_impl_0 {
struct __block_impl impl;
struct __blockStudy_block_desc_0* Desc;
NSObject *__weak weakMyObjetc;
__blockStudy_block_impl_0(void *fp, struct __blockStudy_block_desc_0 *desc, NSObject *__weak _weakMyObjetc, int flags=0) : weakMyObjetc(_weakMyObjetc) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static struct __blockStudy_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __blockStudy_block_impl_0*, struct __blockStudy_block_impl_0*);
void (*dispose)(struct __blockStudy_block_impl_0*);
} __blockStudy_block_desc_0_DATA = { 0, sizeof(struct __blockStudy_block_impl_0), __blockStudy_block_copy_0, __blockStudy_block_dispose_0};
static void __blockStudy_block_copy_0(struct __blockStudy_block_impl_0*dst, struct __blockStudy_block_impl_0*src) {_Block_object_assign((void*)&dst->weakMyObjetc, (void*)src->weakMyObjetc, 3/*BLOCK_FIELD_IS_OBJECT*/);}
block捕获变量时把所有权修饰符也捕获了:NSObject *__weak weakMyObjetc
,可是拷贝的时候_Block_object_assign
传入的同样是3/*BLOCK_FIELD_IS_OBJECT*/
,并没有区分?进一步查看_Block_object_assign
的汇编代码:
___copy_helper_block_: ## @__copy_helper_block_
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp6:
.cfi_def_cfa_offset 16
Ltmp7:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp8:
.cfi_def_cfa_register %rbp
subq $48, %rsp
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movq -16(%rbp), %rsi
movq %rsi, %rdi
movq -8(%rbp), %rax
movq %rax, %rcx
addq $40, %rdi
movq %rcx, %rdx
addq $40, %rdx
movq %rdi, -24(%rbp) ## 8-byte Spill
movq %rdx, %rdi
movq -24(%rbp), %rdx ## 8-byte Reload
movq %rsi, -32(%rbp) ## 8-byte Spill
movq %rdx, %rsi
movq %rcx, -40(%rbp) ## 8-byte Spill
movq %rax, -48(%rbp) ## 8-byte Spill
callq _objc_copyWeak
movq -40(%rbp), %rax ## 8-byte Reload
addq $32, %rax
movq -32(%rbp), %rcx ## 8-byte Reload
movq 32(%rcx), %rdx
movq -48(%rbp), %rsi ## 8-byte Reload
movq $0, 32(%rsi)
movq %rax, %rdi
movq %rdx, %rsi
callq _objc_storeStrong
addq $48, %rsp
popq %rbp
retq
.cfi_endproc
.align 4, 0x90
可以看到它调用了两个方法,分别是_objc_copyWeak
和_objc_storeStrong
,从这里可以看出weak和strong的确是使用了不用的方法对待的。
看下面的例子
NSString *a;
NSLog(@"%p", a);
void (^ myblock)(void) = ^{
// block内部也会有一个指针b 这个指针的值和a这个指针的值是一样的 指向同一个地方 但是并不是同一个指针
// 后面重新给a赋值 修改了a指针的值 也就是修改了a指向的地址 但是并不会影响指针b的值 它指向的还是空
NSLog(@"%@", a); // 输出null
};
a = @"a";
myblock();
block内部打印出的a是null,是因为block内部捕获a的时候,是创建了一个和a指向同一处的指针b,也就是a和b的值相同,但它们不是同一个指针。后面对a赋值的时候,修改了指针a的值,也就是a指向的地址,但是并不会影响b,所以block内部的还是空。
__block NSString *a; // 定义了一个__block结构体 里面有个a指针 __block结构体->forwarding->a
NSLog(@"%p", a);
void (^ myblock)(void) = ^{
NSLog(@"%@", a); // 输出a __block结构体->forwarding->a
};
a = @"a"; // 本质是__block结构体->forwarding->a
// 修改的一直是__block结果体内的a指针的值 __block结构体->forwarding->a
myblock();
用__block来修饰这个变量,本质变成了是在定义一个__block结构体,这个结构体里面包含了我们实际上要声明的变量。后面涉及到这个变量的访问和修改都是通过同一个__block结构体的forwarding来取到的,所以内外的修改能够同步。
void (^ myblock)(void) = ^{
myblock();
};
myblock();
这个代码运行会报错,因为内部的myblock是空的。可以拆分来看:
void (^ myblock)(void);
void (^ tmp)(void) = ^{
myblock();
};
myblock = tmp;
myblock();
其实和上面的逻辑一样,block捕获到的时候这个变量是空的,外部的修改并不会影响内部的变量。可以用一个__block来修饰:
__block void (^ myblock)(int);
void (^ tmp)(int) = ^(int num) {
if (num > 0) {
return;
}
myblock(num - 1);
};
myblock = tmp;
myblock(5);
// __block结构体->forwarding->block变量
// block变量->__block结构体
// 循环引用
这时候可以正常运行了,但是会造成循环引用,block和__block结构体都无法释放。用__weak来打破循环引用:
__block __weak void (^ myblock)(int);
void (^ tmp)(int) = ^(int num) {
if (num < 0) {
return;
}
NSLog(@"%d", num);
myblock(num - 1);
};
myblock = tmp;
myblock(5);
// block变量->弱引用->__block结构体
// __block结构体->forwarding->block变量
// 函数运行结束后 block变量被回收 __block结构体也会被回收
block内部其实是弱引用__block结构体,但函数执行结束后__block结构体就会被回收,看下面这种情况:
__block __weak void (^ myblock)(int);
void (^ tmp)(int) = ^(int num) {
if (num < 0) {
return;
}
NSLog(@"%d", num);
dispatch_async(dispatch_get_main_queue(), ^{
myblock(num - 1);
});
};
myblock = tmp;
myblock(5);
// // block变量->弱引用->__block结构体
// // __block结构体->forwarding->block变量
// // 函数运行结束后 block变量被回收 __block结构体也会被回收
这种情况会报错,因为dispatch_async里面的block执行的时候,myblock其实就是__block结构体已经被回收了,解决方法:
__block __weak void (^ myblock)(int);
void (^ tmp)(int) = ^(int num) {
__strong void (^ strongmyblock)(int) = myblock;
if (num < 0) {
return;
}
NSLog(@"%d", num);
dispatch_async(dispatch_get_main_queue(), ^{
strongmyblock(num - 1);
});
};
myblock = tmp;
myblock(5);
// dispatch_async的block->strongmyblock->myblock 在dispatch_async的block执行之前myblock不会被销毁
dispatch_async的block会持有strongmyblock,而strongmyblock又会持有myblock,所以在dispatch_async的block执行之前myblock不会被释放。