本篇文章中,我们主要来探讨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
的循环引用
。