使用block,出现的主要问题就是循环引用,进而导致内存泄露。
而解决block的循环引用,有两种方法,一种是是将block内部引用的外部对象用weak修饰,另外一种方法是将指针手动置为nil。
然而使用weak修饰外部变量的方法在某些情况下,一旦block内部由于变量由weak修饰导致其内存被释放,在block内部再次使用该对象时由于其已经为nil,所以无法正常调用。这个时候就可以使用weak-strong dance这种方法来避免block内部变量被置为nil。
一 weak-strong dance简介
如下所示,由于控制器对demoBlock有强引用,demoBlock对self即控制器也有强引用, 即出现了循环引用,故在block前将self用weak修饰。但是为了避免在block内部weakSelf因为某种情况被释放后无法使用,故在block内部创建了强指针指向的strongSelf来代替弱指针指向的weakSelf,因为其为局部变量,所以block内码执行完即销毁,但是在代码块执行完是不会轻易销毁的。
__weak typeof(self) weakSelf = self;
self.demoBlock = ^{
ViewController *strongSelf = weakSelf;
NSLog(@"%@",strongSelf.view);
[NSThread sleepForTimeInterval:10.0];
NSLog(@"%@",strongSelf.view);
};
二 实验验证
实验方法参考了如下博客中的实验:原始实验链接
实验目的:
1.实现用weak修饰外部变量,但是该变量在block内部因为某种原因而被销毁,导致无法继续使用该变量。
2.使用weak-strong dance来解决上述变量在block内部被销毁而无法继续使用的问题。
实验思路:
创建2个线程-主线程和一个子线程,主线程在for循环到500时,指针置空,外部引用为0;而该子线程则for循环1000次输出
可能结果:
假如在主线程for循环到500时,block停止执行,即说明该外部变量在block内部被销毁,无法继续使用。
假如在主线程for循环到500时,block仍然成功执行到1000,即说明该外部变量未被销毁,可以正常使用。
实验过程:
没有weak-strong dance下运行代码:
实验类JWPoint中的代码
#import <Foundation/Foundation.h>
typedef void(^SuccBlock)(int data);
@interface JWPoint : NSObject
- (void)networkDataBack;
@end
#import "JWPoint.h"
@implementation JWPoint
- (void)networkDataBack{
__weak __typeof(self)weakSelf = self;
SuccBlock block = ^(int data){
for (int i = 0; i < 1000; i ++) {
[weakSelf go:i];
}
};
block(11);
}
- (void)go:(int)number{
NSLog(@"BLOCK GO %d",number);
}
@end
相关的调用代码
- (void)viewDidLoad {
[super viewDidLoad];
[self blockTest];
}
-(void)blockTest{
__block JWPoint *point = [JWPoint new];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[point networkDataBack];
});
for (int i = 0; i < 501; i ++) {
NSLog(@"Point will die");
if (i == 500) {
point = nil;
NSLog(@"NO REFERENCE");
}
}
NSLog(@"OVER");
}
实验的结果
如下图所示,子线程执行到307次时,主线程的500次循环执行完毕,随后子线程也不再执行。验证了单纯使用weak来修饰block内部引用的外部变量可以解决循环引用的问题,但是无法避免block内部该变量由于是弱指针指向,一旦被销毁无法继续使用的问题。
使用weak-strong dance下的代码:
在block内部,用强指针指向的strongSelf代替弱指针指向的weakSelf。
- (void)networkDataBack{
__weak __typeof(self)weakSelf = self;
SuccBlock block = ^(int data){
JWPoint *strongSelf = weakSelf;
for (int i = 0; i < 1000; i ++) {
[strongSelf go:i];
}
};
block(11);
}
实验结果如下:
当主线程500次循环执行完毕,子线程继续执行,一直执行完1000次循环。说明使用weak-strong dance可以避免block内部弱指针指向的外部变量被销毁的问题。使用这种方法可以完美解决block的循环引用问题。
三 AFNetworking以及ReactiveCocoa中使用weak-strong dance
AFNetworking中使用weak-strong dance
仅在下边这一个地方使用
下边代码出自AFNetworkReachabilityManager.h这个类中
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
ReactiveCocoa中使用weak-strong dance
以下代码出自”NSControl+RACTextSignalSupport.h“类中,分别使用了weakify和strongify在block外部和内部修饰变量。
- (RACSignal *)rac_textSignal {
@weakify(self);
return [[[[RACSignal
createSignal:^(id<RACSubscriber> subscriber) {
@strongify(self);
id observer = [NSNotificationCenter.defaultCenter addObserverForName:NSControlTextDidChangeNotification object:self queue:nil usingBlock:^(NSNotification *note) {
[subscriber sendNext:note.object];
}];
return [RACDisposable disposableWithBlock:^{
[NSNotificationCenter.defaultCenter removeObserver:observer];
}];
}]
map:^(NSControl *control) {
return [control.stringValue copy];
}]
startWith:[self.stringValue copy]]
setNameWithFormat:@"%@ -rac_textSignal", RACDescription(self)];
}