一、不加__block修饰符的情况下在block内部修改变量
1、基本数据类型的auto局部变量

在block中无法修改基本数据类型的auto局部变量,修改时会报Variable is not assignable (missing __block type specifier)错误。block对基本数据类型的auto局部变量是值捕获,无法去修改外部的变量。
2、static修饰的基本数据类型局部变量

如果是static修饰的基本数据类型局部变量,在block内部就能修改外部的变量。block对static修饰的基本数据类型局部变量是指针捕获,在block内部可以修改外部变量而不报错。
3、全局变量
在block中可以修改全局变量,因为全局变量在任何作用域都可以调用,block不会对其进行捕获。
4、对象局部变量

block对对象局部变量是指针捕获,会对其强引用,在内部可以修改变量而不报错
二、__block的作用
1、__block可以用于解决block内部无法修改auto变量值的问题

从2-1中可以看到加了__block修饰后,在block内部修改age就不会报错了
2、__block不能修饰全局变量、静态变量(static)

3、编译器会将__block变量包装成一个对象

2-3加了__block

2-4没加__block
从2-3中可以看到,加了__block后,内部age是一个__Block_byref_age_0对象
三、__block原理
-
编译器会将__block变量包装成一个对象,变成对象后就可以根据指针地址在block内部去修改外部的变量
2-3.png
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
__Block_byref_age_0中包含了isa指针,所以变量为对象。
__forwarding是指向自己的指针
age是变量的值
block通过__forwarding指针去修改age的值
四、__block的内存管理
- 当block在栈上时,并不会对__block变量产生强引用
- 当block被copy到堆时,会调用block内部的copy函数,copy函数内部会调用_Block_object_assign函数,_Block_object_assign函数会对__block变量形成强引用(retain)
- 当blcok从堆中移除时,会调用block内部的dispose函数,dispose函数内部会调用_Block_object_dispose函数,_Block_object_dispose函数会自动释放引用的__block变量(release)
__block int age = 10;
void (^block)(void) = ^{
NSLog(@"%d",age);
};
age = 20;
block();
这里个Block里输出的age为20,因为加了__block修饰符后,编译器会将__block变量包装成一个对象,block对其是指针捕获,可以通知指针去修改值。
- __block去修饰对象时,在oc层面看不出有什么变化,编译成c++时,可以看到内存管理跟没有__block还是有区别的
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_person_0 *person; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_person_0 *_person, int flags=0) : person(_person->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
不加__block时,block是直接强引用的Person对象,但是现在是对__Block_byref_person_0进行了强引用。
struct __Block_byref_person_0 {
void *__isa;
__Block_byref_person_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *person;
};
__Block_byref_person_0内部才是对Person对象强引用,比没有__block多了__Block_byref_person_0这一步。__Block_byref_person_0内部还有一个__Block_byref_id_object_copy和__Block_byref_id_object_dispose,其作用跟block内部的copy和dispose一样,可以滑到上面看block的copy和dispose的讲解。
网友评论