本篇文章中,我们主要来探讨iOS中的Block。
Block的类型
Block主要有三种类型分别为NSGlobalBlock、NSStackBlock、NSMallocBlock
- 1, 当
block不捕获外界变量时,此时其类型为NSGlobalBlock。 - 2,当
局部变量block捕获外界变量,并使用weak修饰时,此时其类型为NSStackBlock。 - 3,当
局部变量block捕获外界变量,不使用weak修饰时,此时其类型为NSMallocBlock。 - 4,当
全局变量block捕获外界变量时,此时其类型为NSMallocBlock。
NSObject *obj = [[NSObject alloc] init];
void (^__weak block)(void) = ^{ // NSStackBlock
NSLog(@"----- %@",obj);
};
self.block = ^(id data) { // NSGlobalBlock
};
self.blockTwo = ^(id data){ // NSMallocBlock
NSLog(@"------ %@", obj);
};
<__NSStackBlock__: 0x7ffeee612430>
<__NSGlobalBlock__: 0x1015ec100>
<__NSMallocBlock__: 0x600001d6bf90>
循环引用
当使用block的时候,我们要注意循环引用的问题,防止内存泄漏,我们来看如下代码
#import "ViewController.h"
typedef void(^LYBlock)(void);
@interface ViewController ()
@property (nonatomic, copy) LYBlock block;
@property (nonatomic, copy) NSString *name;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 循环引用
self.name = @"LY";
self.block = ^(){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",self.name);
});
};
self.block();
}
- (void)dealloc{
NSLog(@"dealloc");
}
这样就产生了循环引用的问题,该ViewController不会被释放,dealloc不会调用。
为什么会产生循环引用?
在这里self引用block,block 引用self即: self -> block ->self,导致其无法释放。
weak-strong dance
为了解决循环引用问题,我们可以使用__weak来修饰self,我们来看如下代码
- (void)viewDidLoad {
[super viewDidLoad];
// 循环引用
self.name = @"LY";
__weak typeof(self) weakSelf = self;
self.block = ^(){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",weakSelf.name);
});
};
self.block();
}
- (void)dealloc{
NSLog(@"dealloc");
}
我们来看其运行结果
[24666:835170] dealloc
[24666:835170] (null)
我们可以看出,__weak解决了循环引用的问题,在异步函数执行之前,ViewController已经销毁,导致运行结果出错。此时,我们需要使用__strong来修饰weak对象。
- (void)viewDidLoad {
[super viewDidLoad];
// 循环引用
self.name = @"LY";
__weak typeof(self) weakSelf = self;
self.block = ^(){
__strong typeof(self) strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",strongSelf.name);
});
};
self.block();
}
- (void)dealloc{
NSLog(@"dealloc");
}
我们来看运行结果:
[24814:841306] LY
[24814:841306] dealloc
这样运行结果就正常了。
中介者模式
下面我们将使用中介者模式进行手动解决循环引用问题。
- (void)viewDidLoad {
[super viewDidLoad];
// 循环引用
self.name = @"LY";
__block ViewController *vc = self;
self.block = ^(){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",vc.name);
vc = nil;
});
};
self.block();
}
- (void)dealloc{
NSLog(@"dealloc");
}
我们来看下运行结果:
[24958:848357] LY
[24958:848357] dealloc
在这里,我们使用了中间变量vc,这样引用关系就变成了self -> block -> vc -> block,在异步函数执行完,我们将中间变量 vc 置为nil,这样就避免了self和block的循环引用。