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