写高质量OC代码52建议总结:40.用块引用其所属对象不要出现保留环

#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.一定要找个适当的时机解决保留环。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值