原文出处: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); } };