作者:cfxiaoxixi連結:https://www.jianshu.com/p/e3ee40181baf
簡介
專案開發時,開發人員經常會遇到一種情況,A控制器push進入B控制器,B控制器正在進行網路請求,請求未結束時,點選傳回回到A控制器,現在問題出現了,B中網路請求還在執行,dealloc並未立即呼叫,為什麼會發生這種情況?想在退出當前控制器時取消掉正在進行的請求,怎麼做?
網路請求的封裝
以AFNetworking為例,上我自己的網路請求封裝主要程式碼:
//單例樣式
+ (HttpManager *)sharedManager
{
static dispatch_once_t once;
dispatch_once(&once;, ^{
httpManager = [[HttpManager alloc] init];
});
return httpManager;
}
//網路類初始化
- (id)init{
self = [super init];
if(self)
{
manager = [AFHTTPSessionManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
}
return self;
}
註意既然封裝為單例,manager在專案執行期間就只用初始化一次,所以我把它宣告為了一個成員變數。現在來看看我的網路請求呼叫形式:
[[HttpManager sharedManager] dataFromWithBaseURL:BaseURL path:url method:@"POST" timeInterval:10 params:parmas success:^(NSURLRequest *request, NSURLResponse *response, id JSON) {
} failure:^(NSURLRequest *request, NSURLResponse *response, NSError *error, id JSON) {
} error:^(id JSON) {
} finish:^(id JSON) {
}];
相信大部分開發者的封裝格式都是類似的,個別反人類的封裝格式我也沒遇到過。當我在B呼叫網路請求時,突然傳回到A(此時B中請求還在執行),但是B中dealloc方法並未立即呼叫,等過幾秒種後,B中請求的資料傳回了,然後dealloc才呼叫。相信很多同學都遇到過這種情況,有人會想是否是block裡面有物件產生了迴圈取用,才發生這種情況。我想說的是,如果真是迴圈取用,那就會導致控制器的dealloc方法一直不呼叫,而不是上述情況。
在我看來,應該是使用了這種Block格式發起的網路請求後,由於還在執行,block裡面並未得到響應,所以該block對當前的控制器,有一種強取用的效果,導致控制器退出後,並未釋放掉,直到請求資料傳回,block裡面得到響應,才算完成,最終呼叫dealloc方法。
當然,這隻是我的見解,有不對的地方請指出來。
取消正在進行的網路請求
上面講了一大堆廢話,現在來講正題了。由於上述情況的原因,導致我們開發的app在一些非常規操作上,會產生一些不友好的效果。現在要求就是在退出控制器B後,取消還在B中進行的網路請求。
要求清晰了,那麼要怎麼實現,其實很簡單,貼上我的程式碼:
- (void)cancelRequest
{
if ([manager.tasks count] > 0) {
NSLog(@"傳回時取消網路請求");
[manager.tasks makeObjectsPerformSelector:@selector(cancel)];
//NSLog(@"tasks = %@",manager.tasks);
}
}
不要每次請求的時候都去初始化manager(AFHTTPSessionManager)物件,tasks裡面裝的就是正在進行的網路請求,來一張圖就理解了:
manager裡面的tasks裝的就是正在執行的網路請求。執行cancel後,tasks就會清空,網路請求會進入失敗的狀態,然後響應failure block,得到一個error的資訊,表示請求已經成功取消了。
至於後臺伺服器是否會因為中斷請求受影響,空閑的同學可以去測試下。
最後
還有一些其它取消請求的方法,就不列出來了,因為要不就沒成功,要不就很麻煩,在這就用了一種我認為最簡單的方法。