#import <Foundation/Foundation.h>
typedef void(^EOCNetworkFetcherCompletionHandler)(NSData *data);
@interface EOCNetworkFetcherTwo : NSObject
@property (nonatomic, strong, readonly) NSURL *url;
-(id)initWithURL:(NSURL *)url;
-(void)startWithCompletionHandler:(EOCNetworkFetcherCompletionHandler)completion;
@end
#import "EOCNetworkFetcherTwo.h"
@interface EOCNetworkFetcherTwo ()
@property (nonatomic, strong, readwrite) NSURL *url;
@property (nonatomic, copy) EOCNetworkFetcherCompletionHandler completionHandler;
@property (nonatomic, strong) NSData *downLoadedData;
@end
@implementation EOCNetworkFetcherTwo
-(id)initWithURL:(NSURL *)url{
self = [super init];
if (self) {
_url = url;
}
return self;
}
-(void)startWithCompletionHandler:(EOCNetworkFetcherCompletionHandler)completion{
self.completionHandler = completion;
// 获取数据后(_downLoadedData),调用p_requestCompleted
}
-(void)p_requestCompleted {
if (_completionHandler) {
_completionHandler(_downLoadedData);
}
}
@end
#import "EOCClassTwo.h"
#import "EOCNetworkFetcherTwo.h"
@implementation EOCClassTwo {
EOCNetworkFetcherTwo *_networkFetcher;
NSData *_fetchedData;
}
-(void)downloadData {
NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/something.dat"];
_networkFetcher = [[EOCNetworkFetcherTwo alloc] initWithURL:url];
[_networkFetcher startWithCompletionHandler:^(NSData *data) {
_fetchedData = data;
}];
}
这段代码中有一个保留环,CompletionHandler块要设置_fetchedData变量,所以捕获并保留了self。EOCClassTwo(self)通过strong实例变量保留了获取器(EOCNetworkFetcherTwo),EOCNetworkFetcherTwo的属性是CompletionHandler。三个对象互相引用。
打破方法:要么令_networkFetcher不再引用获取器,要么令获取器的CompletionHandler属性不再持有handler块。
比如块中的代码可以这么改
[_networkFetcher startWithCompletionHandler:^(NSData *data) {
_fetchedData = data;
_networkFetcher = nil;
}];
一般来说只要在合适的时机清理掉环中的某个引用就可以解决问题,但是未必总有这个机会。以上为例,如果块一直不运行,保留环会一直存在。
改下代码,EOCClassTwo不需要引用EOCNetworkFetcherTwo。
-(void)downloadDataTwo {
NSURL *url = [[NSURL alloc] initWithString:@"http://www.example.com/something.dat"];
EOCNetworkFetcherTwo *networkFetcher = [[EOCNetworkFetcherTwo alloc] initWithURL:url];
[networkFetcher startWithCompletionHandler:^(NSData *data) {
_fetchedData = data;
}];
}
这种情况的保留环很难被发现,块需要用过获取器(EOCNetworkFetcherTwo)引入url,EOCNetworkFetcherTwo通过CompletionHandler属性持有块。
只需要将p_requestCompleted代码修改下。
-(void)p_requestCompleted {
if (_completionHandler) {
_completionHandler(_downLoadedData);
}
self.completionHandler = nil;
}
总结:
1.如果块所捕获的对象直接或间接的保留了块本身,就需要注意保留环的问题。
2.一定要找个适当的时机解决保留环。