iOS arc下循环引用问题

原文出处:http://blog.cnbang.net/tech/2085/ xcode

本身理解框架

开发时,用老框架ASIHttpRequest请求时(如今使用AFNetWorking),有时候会出现发不出请求的状况,项目开启了ARC,代码以下:fetch

@implement MainController
- (void) fetchUrl{
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:currUrl]];
    [request setCompletionBlock:^{
        NSLog(@"completed");
    }];
    [request startAsynchronous];
}
@end

排查后发现,request这个对象在执行完setCompletionBlock方法后,被释放掉了,也就不会执行[request startAsynchronous]这里,天然也发不出请求。
this

解决办法:atom

因为使用了ARC,也无法手动调用[request remain]让这个变量不被释放,因此只能将这个变量改为实例变量,让Controller存在时一直持有该对象不被释放。spa

修改为以下:.net

@interface MainController {
     ASIHTTPRequest *request;
}
@end
 
@implement MainController
- (void) fetchUrl{
    request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:currUrl]];
    [request setCompletionBlock:^{
        [self complete];
    }];
    [request setFailedBlock:^{
          NSLog(@"failed");
    }];
    [request startAsynchronous];
}
@end

这样又有新的问题了code

XCode编译后提示[self complete]这一行可能会致使循环引用对象

重点理解“循环引用”处来了
blog

由于MainController实例对象持有属性request,而request持有方法setCompletionBlock,可是setCompletionBlock方法里面又持有MainController实例对象,这样就致使循环引用,MainController实例对象在外面引用计数为0时仍然没法释放,由于request里面持有MainController实例对象的引用,其引用计数永远大于1。

致使循环引用的缘由在于setCompletionBlock里面调用的self是一个strong类的引用,会使self引用计数+1。解决方法就是声明一个__weak变量指向self,这样block使用这个变量时就不会致使self引用计数+1,不会致使循环引用。

@implement MainController
- (void) fetchUrl{
     request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:currUrl]];
     __weak id this = self;
 
    [request setCompletionBlock:^{
        [this complete];
    }];
    [request startAsynchronous];
}
@end

新的问题,在block中若是只是调用MainController的方法,上面就能完美解决问题,可是如今须要在block中调用不少实例变量,包括赋值等,以下

@interface MainController {
     ASIHTTPRequest *request;
     BOOL isLoading;
     UIView *loadingView;
}
@end
 
@implement MainController
- (void) fetchUrl{
    request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:currUrl]];
 
    [request setCompletionBlock:^{
        isLoading = NO;
        loadingView.hidden = NO;
    }];
    [request startAsynchronous];
}
@end

xcode提示说isLoading = NO和loadingView.hidden = NO两行可能循环引用,解决方法以下:

实例变量所有加上get set方法,经过弱引用对象访问便可,缺点是破坏了封装性,把本来私有的实例变量变成公有的。

@interface MainController {
     ASIHTTPRequest *request;
}
@property (nonatomic, strong) UIView *loadingView;
@property (nonatomic, assign) BOOL isLoading;
@end
 
@implement MainController
@synthesize loadingView, isLoading;
 
- (void) fetchUrl{
     request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:currUrl]];
     __weak id this = self;
 
    [request setCompletionBlock:^{
        this.isLoading = NO;
        this.loadingView.hidden = NO;
    }];
    [request startAsynchronous];
}
@end


新框架(AFNetworking)以下方法解决上述问题

__weak __typeof(&*self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(&*weakSelf)strongSelf = weakSelf;
if (!strongSelf) {
return;
}
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
相关文章
相关标签/搜索