通过这篇文章,可以让小伙伴能理解Block的使用及注意细节,也让Block代码块在开发中运用得更加恰到好处,本人通过自己的理解来讲解这篇文章,若有错误,欢迎指正!
一、原理
typedef void(^myBlock)(void);
myBlock block = ^{
printf("helloWorld");
};
block();
- 我们将代码转换为C++源码来进行详细地分析
// 进行适当缩减,易阅读
myBlock block = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA));
((__block_impl *)block)->FuncPtr)((__block_impl *)block);
- 通过调用__main_block_impl_0函数传入参数来初始化生成一个block对象。
- __block_impl
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
__block_impl结构体存放着类对象以及block函数实现。
- isa:指向类对象地址;
- Flags:标记,默认为0;
- Reserved:今后版本升级所需的区域;
- FuncPtr:指向实现函数地址;
- __main_block_desc_0
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)};
__main_block_desc_0结构体用于计算存放内存空间大小。
- reserved:今后版本升级所需的区域;
- Block_size:block所需占用内存大小;
- __main_block_impl_0
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;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("helloWorld");
}
- 将__main_block_func_0实现函数地址传入指向FuncPtr;
- 将block块内存大小初始化Desc;
二、本质
结论:Block本质是OC对象!
- ?

- block中的isa指针指向类__NSMallocBlock__;
- 根据runtime源码,我们知道Class就是结构体objc_class,而objc_class又继承自objc_object结构体,所以可得block本质就是一个对象。
- ?
Block block = ^{
NSLog(@"helloWorld");
};
NSLog(@"%@", [block class]);
NSLog(@"%@", [[block class] superclass]);
NSLog(@"%@", [[[block class] superclass] superclass]);
NSLog(@"%@", [[[[block class] superclass] superclass] superclass]);
//运行结果
// __NSGlobalBlock__
// __NSGlobalBlock
// NSBlock
// NSObject
- 根据运行结果可清楚得看到block的父类;
- block最终继承自NSObject,所以这里我们可以更加确信我们的结论!
三、捕获变量及对象
- 对象及变量存放区域

- 未使用到局部变量的block则是__NSGlobalBlock__类型;
- 使用到局部变量的block则是__NSMallocBlock类型,需要进行内存管理;
- __NSStackBlock__是存放在栈上的,受作用域所影响,超过作用域后内存将会被回收。
- 捕获局部变量
int var = 100;
void(^block)(void) = ^{
//var = 200; 不能修改var的值
NSLog(@"var:%d", var);
};
var = 200;
block();
// 转换部分源码
struct __main_block_impl_0 {
//..省略部分源码..
int var;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _var, int flags=0) : var(_var) {
//..省略部分源码..
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int var = __cself->var; // bound by copy
NSLog(var);
}
int main(int argc, const char * argv[]) {
{
int var = 100;
void(*block)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, var));
}}
- block捕获局部变量后,不能在block中修改局部变量var的值;
- 从源码知道,block在内部重新生成一个相同的变量引用着局部变量var的值,在这里变量var只是将值传递给block,所以在block内部不能修改重新给变量var赋值;因为是变量var值传递,局部变量在外部修改不会对block内部产生影响。
- 捕获static变量
static int var = 100;
void(^block)(void) = ^{
NSLog(@"var:%d", var);
var = 200;
NSLog(@"var:%d", var);
};
block();
// 转换源码
struct __main_block_impl_0 {
...
int *var;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_var, int flags=0) : var(_var) {
...
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int *var = __cself->var; // bound by copy
NSLog((*var));
(*var) = 200;
NSLog((*var));
}
int main(int argc, const char * argv[]) {
{
static int var = 100;
void(*block)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, &var));
((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
}
- 变量加上static后,传递过程为变量地址;
- block内部生成一个指针var指向静态变量var的地址,所以在block内部可直接修改变量var的值,直接对指向变量地址的指针变量进行赋值;
- 捕获全局变量
static int var = 100;
int globalVar = 100;
void(^block)(void) = ^{
NSLog(@"var:%d, globalVar: %d", var, globalVar);
var = 200;
globalVar = 200;
NSLog(@"var:%d, globalVar: %d", var, globalVar);
};
block();
//转换源码
static int var = 100;
int globalVar = 100;
..
struct __main_block_impl_0 {
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
..
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSLog(var, globalVar);
var = 200;
globalVar = 200;
NSLog(var, globalVar);
}
int main(int argc, const char * argv[]) {
{
void(*block)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA));
((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
- 对于全局变量,block中不会有任何变量的初始化传递;
- 全局变量在block直接使用,block中不会生成任何变量;
- 捕获对象
JZPerson* person = [JZPerson new];
person.name = @"hello";
void(^block)(void) = ^{
NSLog(@"name: %@", person.name);
};
block();
// 转换源码
struct __main_block_impl_0 {
JZPerson *person;
...
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, JZPerson *_person, int flags=0) : person(_person) {
...
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
JZPerson *person = __cself->person; // bound by copy
NSLog(person, sel_registerName("name")));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->person, (void*)src->person, 3 /*BLOCK_FIELD_IS_OBJECT*/);
}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
int main(int argc, const char * argv[]) {
{
JZPerson* person = (objc_msgSend(objc_getClass("JZPerson"), sel_registerName("new"));
void(*block)(void) = (&__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, person, 570425344));
(((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
- block中生成person的强引用指向person对象;
- person对象类似于局部变量,只是将person对象传入block并用__main_block_impl_0结构体中的person进行初始化,我们只可以访问对象的属性值,而不能在block中将外部的person对象重新赋值。
- __block使用
当我们需要在block内部对变量或者对象进行重新赋值修改时,对于变量,我们可以使用static进行修饰进行修改;而对于对象,OC提供了__block修饰符给我们使用,当然了__block也可以修饰变量!
?
__block int var = 100;
void(^block)(void) = ^{
NSLog(@"name: %d", var);
};
block();
//转换源码
struct __Block_byref_var_0 {
__Block_byref_var_0 *__forwarding;
int var;
...
};
struct __main_block_impl_0 {
...
__Block_byref_var_0 *var; // by ref
__Block_byref_var_0 *_var, int flags=0) : var(_var->__forwarding) {
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_var_0 *var = __cself->var; // bound by ref
NSLog(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 {
...
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[]) {
__attribute__((__blocks__(byref))) __Block_byref_var_0 var = {0,(__Block_byref_var_0 *)&var, 0, sizeof(__Block_byref_var_0), 100};
void(*block)(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;
}
- 使用__block修饰后block内部重新生成__Block_byref_var_0结构体,并将变量初始化为一个结构体复制到__main_block_impl_0结构体中var;
- __Block_byref_var_0中定义了一个相同类型的属性__forwarding,__forwarding指向了对象本身,代码中初始化为&var;
- __forwarding作用
我们在MRC下测试__forwarding的作用,因为MRC下block是在栈上,而ARC下生成的block是在堆上!
?一:
__block int var = 100;
NSLog(@"%p", &var);
void(^block)(void) = ^{
NSLog(@"%p", &var);
};
block();
NSLog(@"%p", &var);
/** 结果 **/
//0x7ffeefbff468
//0x7ffeefbff468
//0x7ffeefbff468
?二:
__block int var = 100;
NSLog(@"%p", &var);
void(^block)(void) = ^{
NSLog(@"%p", &var);
};
[block copy];
block();
NSLog(@"%p", &var);
/** 结果 **/
//0x7ffeefbff468
//0x103108058
//0x103108058
- 例一通过打印地址在MRC下变量var是在栈上的,block内部使用的是外部的var;
- 例二将block执行copy,会将block复制到堆上!同时,block也将变量var也一并复制到了堆上;
作用:
- 方便内存管理!
- 通过__forwarding指针指向了自身,可通过__forwarding将var变量进行修改;
- 当block在栈上时,__forwarding指向的是自身;
- 当block在堆上时,__Block_byref_var_0也会复制一份到堆上,此时栈上的__forwarding将不再指向自己,而是指向了堆上的__forwarding,堆上的__forwarding则是指向自己!!
copy到堆上时的__forwarding:

- __block内存管理
- 当block在栈上时,则block使用的变量也是在栈上,无需对变量或对象进行内存管理;
- 当block复制到堆上,则block中使用__main_block_copy_0将对象或变量进行内存管理,当对象为strong时,则将对象引用计数加1;当对象为weak时,则不增加对象引用计数。当对象释放时,相应地会调用__main_block_dispose_0进行减1操作,对象的释放还是要看其是否被其它对象所引用!
block复制到堆上:

block释放:

参考文章:
2111

被折叠的 条评论
为什么被折叠?



