多執行緒-NSOperation中使用ASIHttpRequest注意事項

scorpiozj發表於2013-06-21

最近做的iPhone專案中有一如下功能:

app在使用者許可後將本地Photos的照片上傳到伺服器,期間使用者可以做其他任何操作,等上傳成功後彈出一個toast通知使用者。

原先的程式碼結構是:

  1. 獲取照片的操作放在NSOperation的子類A中
  2. 獲取完照片後,逐個生成一個上傳類B(此上傳類是ASIFormDataRequest的子類),並把它新增到NSOperationQueue中。

其中operationqueue設定了最大執行數是1,但是實際測試下來發現所有的上傳都是併發的,一查程式碼,發現上傳類B居然沒有實現main,就一個init函式。初始化完之後直接startAsynchronous了,然後返回self。真是奇葩~~

於是將上傳類B修改,新增了main函式,但是執行的時候出錯:

- (void)reportFinished
{
    if (delegate && [delegate respondsToSelector:didFinishSelector]) {
        [delegate performSelector:didFinishSelector withObject:self];
    }---------------------->提示bad_access的錯誤

    #if NS_BLOCKS_AVAILABLE
    if(completionBlock){
        completionBlock();
    }
    #endif

    if (queue && [queue respondsToSelector:@selector(requestFinished:)]) {
        [queue performSelector:@selector(requestFinished:) withObject:self];
    }
}

檢視delegate的值,發現已經overrelease了。B在設定的時候,將delegate設定為A的例項了,A的例項怎麼會不等B的返回就結束了呢?

原來A本身是一個operation,假設執行在次執行緒 M中。B因為是繼承ASIFormDataRequest,其實也是一個NSOperation,也就是說B執行的時候也是執行在次執行緒N中的。因為B使用的是非同步執行,N必然不同於M。而A在將上傳操作結束完以後,就結束了,系統就會回收A的記憶體。這個時候在N中執行的B尚未收到響應。等到response返回的時候,A早就已經釋放了,所以就會有如上的錯誤。

怎麼解決呢?有同事是把A設定為property。這樣可以解決,但是當需要呼叫A的類很多的時候,就會比較麻煩。

其實解決的辦法很簡單,就是在A中過載isFinished方法,當確定所有的照片上傳上去後返回YES否則返回NO,這樣我們就可以控制A,避免系統“過早”的釋放。

與此同時我們發現,ASIHttpRequest的delegate響應都會路由到主執行緒:

- (void)requestFinished
{
#if DEBUG_REQUEST_STATUS || DEBUG_THROTTLING
    NSLog(@"[STATUS] Request finished: %@",self);
#endif
    if ([self error] || [self mainRequest]) {
        return;
    }
    if ([self isPACFileRequest]) {
        [self reportFinished];
    } else {
        [self performSelectorOnMainThread:@selector(reportFinished) withObject:nil waitUntilDone:[NSThread isMainThread]];
    }
}

stackoverflow上有關於這樣做的討論,這裡要說明的是因為B是執行在後臺,delegate是A,不需要在主執行緒響應。我們可以在B中過載上述函式,將performSelectorOnMainThread:函式去掉,直接呼叫reportFinished。

進一步考慮,iOS上獲取本地照片現在一般用ALAssetsLibrary,這個庫一般是用block去列舉,換言之獲得照片內容的操作已經是在次執行緒中操作的了。

這樣一來A也就可以不需要是NSOperation,是個一般的NSObject即可。

 

最近專案新增了很多“奇葩”的功能,可是參與的決定權不在自己這邊,雖然我列出了很多不應該這樣做的理由和依據。但是需求人員都以本國的使用者需求為藉口——看來公司越來越成為外資公司在華的外包公司了。這個職位也變得越來越乏味,雖然不見得能馬上跳槽,但是也學會了在“逆境”中堅強:學習產品的設計,和非開發人員的溝通,重構程式碼。覺得有句話說的真好:要想做自己想做的事,就得先做自己不想做的事。

與諸君共勉!

 

相關文章