在没有访问局部变量的情况下,Block的本质类似于一个闭包,它具有参数和返回值,并可以像普通函数一样存储在全局区,被称为"全局Block"。
如果我们在一个函数内部创建了一个block,该block内部代码引用了该函数内的某些局部变量(此时该block为栈block(stackBlock)
),为了防止在block调用时引用的局部变量在原始作用域被销毁,我们需要把block以及该局部变量copy到堆上(此时该block为堆block(mallocBlock)
),以确保block可以被正常执行。
auto变量 & static变量
// 在声明变量时,如果没有添加static关键字,那么默认为auto变量,在函数调用结束后自动销毁
(auto) int a = 10;
// 静态变量,储存于全局区,方法调用结束后不会销毁
static int a = 10;
1.全局block
如果block中没有访问任何外部变量,则将被存储在全局区中,不会被销毁(这种不访问外部变量的情况下我们一般会直接定义方法)
- (void)viewDidLoad {
[super viewDidLoad];
void (^block1)(void) = ^{};
NSLog(@"%@", [block1 class]); // __NSGlobalBlock__
}
2.MRC模式中的栈block
如果block中引用了auto变量
, 那么block的将被存储在栈中。在MRC模式下方法调用结束后block的内存会被回收。
- (void)viewDidLoad {
[super viewDidLoad];
int age = 10;
void (^block1)(void) = ^(){
NSLog(@"%d", age);
};
NSLog(@"%@", [block1 class]); // __NSStackBlock__
}
2.1 MRC模式中栈block导致的问题
由于stackBlock
引用的block在getBlock
方法执行完成后就被销毁了,所以当在viewDidLoad
方法中调用stackBlock
时,age
的值发生了错误
#import "ViewController.h"
typedef void (^Myblock)(void);
Myblock stackBlock;
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self getBlock];
// __NSStackBlock__
NSLog(@"%@", [stackBlock class]);
// -307953584
stackBlock();
}
- (void)getBlock {
int age = 100;
stackBlock = ^(){
NSLog(@"%d", age);
};
}
@end
2.1 解决MRC模式中栈block导致的问题
在getBlock
方法中将栈block
赋值给stackBlock
时,我们对栈block进行一次copy
操作,这样就会将该栈block的副本存储在堆中,该副本不会随着方法执行结束而销毁。
- (void)viewDidLoad {
[super viewDidLoad];
[self getBlock];
// __NSMallocBlock__
NSLog(@"%@", [stackBlock class]);
// -100
stackBlock();
}
- (void)getBlock {
int age = 100;
stackBlock = [^(){
NSLog(@"%d", age);
} copy];
}
3 堆block
在MRC模式下,堆Block是由栈Block拷贝而来,需要程序员手动调用copy方法来实现。
而在ARC模式下,为了安全起见,xcode会对栈block的赋值行为自动进行一次copy操作(即使赋值给以Strong修饰的成员变量,依然会进行一次copy操作,在堆中创建一个新副本)
网友评论