目录
一,本质
二,变量捕获
三,类型
四,对象类型的auto变量
一,本质
1,实例代码
int main(int argc, const char * argv[]) {
@autoreleasepool {
void(^block)(void) = ^{
NSLog(@"hello block!");
};
block();
}
return 0;
}
2,底层代码(用clang
进行转换)
main
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
// 创建(调用构造函数)
void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
// 调用(执行FuncPtr指针指向的函数)
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
__main_block_impl_0
// 底层结构
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
// 构造函数(第一个参数是__main_block_func_0,第二个参数是__main_block_desc_0)
__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;
}
};
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr; // 指向__main_block_func_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_func_0
// block中需要执行的代码
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_0d_7m56tzw16qs_vsxqtxhnsq2m1myhmv_T_main_887410_mi_0);
}
3,结构图
结构图4,结论
-
block
的本质是结构体 -
block
也是OC
对象,因为它内部有isa
指针
二,变量捕获
1,实例代码
// 全局变量
int age3 = 3;
int main(int argc, const char * argv[]) {
@autoreleasepool {
// auto变量(auto可以省略)
auto int age1 = 1;
// static变量
static int age2 = 2;
void(^block)(void) = ^{
NSLog(@"%d---%d---%d", age1, age2, age3);
};
age1 = 4;
age2 = 5;
age3 = 6;
block();
}
return 0;
}
// 打印
1---5---6
2,底层代码
main
int age3 = 3;
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
auto int age1 = 1;
static int age2 = 2;
// age1传值,age2传地址
void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age1, &age2));
age1 = 4;
age2 = 5;
age3 = 6;
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
__main_block_impl_0
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age1; // 保存外部age1的值
int *age2; // 保存外部age2的地址
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age1, int *_age2, int flags=0) : age1(_age1), age2(_age2) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
__main_block_func_0
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
// 取出age1的值
int age1 = __cself->age1; // bound by copy
// 取出age2的地址
int *age2 = __cself->age2; // bound by copy
// *age2取出地址中的值,age3直接访问
NSLog((NSString *)&__NSConstantStringImpl__var_folders_0d_7m56tzw16qs_vsxqtxhnsq2m1myhmv_T_main_10171a_mi_0, age1, (*age2), age3);
}
3,结论
变量类型 | 是否捕获到block内部 | 访问方式 |
---|---|---|
auto(局部) | YES | 值传递 |
static(局部) | YES | 指针传递 |
全局 | NO | 直接访问 |
- 局部变量和全局变量
由上面底层代码可知
1>age1和age2是定义在main
函数里面的,而在__main_block_func_0
函数中访问它们,这种跨函数访问必须先捕获,先把变量保存到block
中,在访问时再从block
中取出
2>age3是定义在所有函数之外的,任何函数都可以访问,不用捕获
-
auto
变量和static
变量
1>
auto
变量超出作用域就会销毁,block
可能在其销毁后才调用,为了保证能访问到该变量,只能先把变量值捕获进来
2>static
变量超出作用域不会销毁,可以直接捕获变量地址,在访问时用变量地址取值即可
4,注意点
self
是局部变量,会被捕获到block
内部
- 实例代码
@implementation Person
- (void)eat {
void(^block)(void) = ^{
NSLog(@"%@", self);
};
block();
}
@end
- 底层代码
// self是作为参数传递进来的,并不是全局变量
static void _I_Person_eat(Person * self, SEL _cmd) {
void(*block)(void) = ((void (*)())&__Person__eat_block_impl_0((void *)__Person__eat_block_func_0, &__Person__eat_block_desc_0_DATA, self, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
struct __Person__eat_block_impl_0 {
struct __block_impl impl;
struct __Person__eat_block_desc_0* Desc;
Person *self; // 指向外部self
__Person__eat_block_impl_0(void *fp, struct __Person__eat_block_desc_0 *desc, Person *_self, int flags=0) : self(_self) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
三,类型
1,三种类型
- 实例代码
// MRC环境
void(^block1)(void) = ^{
NSLog(@"hello block!");
};
NSLog(@"block1---%@", [block1 class]);
auto int age = 1;
void(^block2)(void) = ^{
NSLog(@"%d", age);
};
NSLog(@"block2---%@", [block2 class]);
void(^block3)(void) = [^{
NSLog(@"%d", age);
} copy];
NSLog(@"block3---%@", [block3 class]);
// 打印
block1---__NSGlobalBlock__
block2---__NSStackBlock__
block3---__NSMallocBlock__
- 结论
NSGlobalBlock
:没有访问auto
变量
NSStackBlock
:访问了auto
变量
NSMallocBlock
:NSStackBlock
调用copy
2,copy
操作
block类型 | 内存区域 | 释放时刻 | copy结果 |
---|---|---|---|
NSGlobalBlock | 全局静态区 | 程序结束系统释放 | 什么也不做 |
NSStackBlock | 栈 | 超出作用域系统释放 | 从栈复制到堆 |
NSMallocBlock | 堆 | 程序员手动释放 | 引用计数增加 |
⚠️注意⚠️:NSStackBlock
超出作用域就会释放,如果在作用域外使用的话会crash,所以需要copy
到堆上,这也是block
要用copy
修饰的原因,不过ARC环境下系统会自动copy
到堆上,所以用strong
修饰也可以
3,ARC环境下系统自动将栈上的block
复制到堆上的情况
- 作为函数返回值时
typedef void(^Block)(void);
Block test() {
int age = 1;
return ^{
NSLog(@"%d", age);
};
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
Block block = test();
NSLog(@"%@", [block class]);
}
return 0;
}
// 打印
__NSMallocBlock__
- 赋值给
__strong
指针时
typedef void(^Block)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = 1;
// 默认就是__strong
__strong Block block = ^{
NSLog(@"%d", age);
};
NSLog(@"%@", [block class]);
}
return 0;
}
// 打印
__NSMallocBlock__
- 作为Cocoa API中方法名含有
usingBlock
的方法参数时
NSArray *array = [NSArray new];
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
// 此block为NSMallocBlock
}];
- 作为
GCD API
的方法参数时
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 此block为NSMallocBlock
});
四,对象类型的auto变量
1,栈上的block
不会强引用auto
变量
- 实例代码
// MRC环境
typedef void(^Block)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [Person new];
Block block = ^{
NSLog(@"%@", person);
};
[person release];
NSLog(@"block未释放:%@", [block class]);
}
return 0;
}
// 打印
Person dealloc
block未释放:__NSStackBlock__
- 底层代码
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
Person *person; // 指向外部person
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *_person, int flags=0) : person(_person) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
- 分析
block
捕获了person
对象,但block
还未释放person
就已经销毁了,所以block
对person
没有产生强引用
2,堆上的block
会强引用auto
变量
- 实例代码
typedef void(^Block)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
Block block;
{
Person *person = [Person new];
block = ^{
NSLog(@"%@", person);
};
}
NSLog(@"block未释放:%@", [block class]);
}
return 0;
}
// 打印
block未释放:__NSMallocBlock__
Person dealloc
- 底层代码
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
Person *__strong person; // 指向外部person
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__strong _person, int flags=0) : person(_person) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
- 分析
person
对象超出作用域并没有立即销毁,而是在block
释放之后才销毁,所以block
对person
产生了强引用
3,如果用__weak
修饰auto
变量,堆上的block
对auto
变量就是弱引用了
- 实例代码
typedef void(^Block)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
Block block;
{
Person *person = [Person new];
__weak Person *weakPerson = person;
block = ^{
NSLog(@"%@", weakPerson);
};
}
NSLog(@"block未释放:%@", [block class]);
}
return 0;
}
// 打印
Person dealloc
block未释放:__NSMallocBlock__
- 底层代码
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
Person *__weak weakPerson; // 指向外部weakPerson
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
- 分析
person
对象超出作用域就立即销毁了,而block
还没有释放,所以block
对person
是弱引用
4,copy
和dispose
- 底层代码
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
// 指向__main_block_copy_0
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
// 指向__main_block_dispose_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};
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
// 强/弱引用person
_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) {
// 释放person
_Block_object_dispose((void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
- 分析
block
捕获的auto
变量如果是对象类型的,在__main_block_desc_0
函数中会多两个指针,在底层代码中会多两个函数,用来管理该对象的内存
1>
__main_block_copy_0
:当block
复制到堆上时会通过copy
指针调用此函数,此函数会对auto
变量产生强/弱引用
2>__main_block_dispose_0
:当block
从堆上移除时会通过dispose
指针调用此函数,此函数会释放引用的auto
变量
5,实例练习
- 实例一
- (void)viewDidLoad {
[super viewDidLoad];
Person *person = [Person new];
__weak typeof(person) weakPerson = person;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"block1---%@", weakPerson);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"block2---%@", person);
});
});
NSLog(@"person作用域结束");
}
// 打印
14:30:21 person作用域结束
14:30:22 block1---<Person: 0x600002774990>
14:30:24 block2---<Person: 0x600002774990>
14:30:24 Person dealloc
- 实例二
- (void)viewDidLoad {
[super viewDidLoad];
Person *person = [Person new];
__weak typeof(person) weakPerson = person;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"block1---%@", person);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"block2---%@", weakPerson);
});
});
NSLog(@"person作用域结束");
}
// 打印
14:33:06 person作用域结束
14:33:07 block1---<Person: 0x6000011cc450>
14:33:07 Person dealloc
14:33:09 block2---(null)
网友评论