2013-01-17 21 views
15

我是GCD和新手,並且正在緩解這一問題。GCD - 用於更新UIImageView的主要vs後臺線程

背景:我正在爲使用ALAssetsLibrary的UIScrollView的延遲加載例程工作。當我的UIScrollView加載時,我使用我的ALAssets的aspectRatioThumbnails填充它,然後在用戶滾動時,我調用以下例程以加載當前正在顯示的ALAsset的fullScreenImage。它似乎工作。

(如果任何人有更好的延遲加載程序請發表評論。我看了所有我能找到加上WWDC的視頻,但他們似乎與平鋪處理更多或有更多的複雜性比我更需要)

我的問題:我使用一個後臺線程來處理加載fullScreenImage,當做到這一點我用的是主線程,將其應用到UIImageView的。 我是否需要使用主線程?我已經看到所有UIKit更新都需要在主線程上發生,但我不確定這是否適用於UIImageView。我以爲它確實如此,因爲它是一個屏幕元素,但後來我意識到我根本不知道。

- (void)loadFullSizeImageByIndex:(int)index 
{ 
    int arrayIndex = index; 
    int tagNumber = index+1; 
    ALAsset *asset = [self.assetsArray objectAtIndex:arrayIndex]; 

    __weak typeof(self) weakSelf = self; 

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ 
     UIImage *tmpImage = [[UIImage alloc] initWithCGImage:asset.defaultRepresentation.fullScreenImage]; 

     if ([weakSelf.scrollView viewWithTag:tagNumber] != nil){ 

      dispatch_async(dispatch_get_main_queue(), ^{ 

       if ([weakSelf.scrollView viewWithTag:tagNumber]!= nil){ 
        UIImageView * tmpImageView = (UIImageView*)[weakSelf.scrollView viewWithTag:tagNumber]; 
        tmpImageView.image = tmpImage; 
       } 
      }); 
     } 
    }); 
} 
+0

是它在渲染圖像,當你把在後臺線程點檢查強爲非零? – iDev

+0

@ACB:剛做了一個測試 - 是的,它的渲染速度比從主線程調用慢5倍左右(如我的示例代碼所示)。 –

+0

但是按照UIView文檔,你應該在主線程中使用它。我在答覆中從文檔中發佈了該部分。 – iDev

回答

32

是的,你需要使用每當你觸摸UIImageView主線程,或任何其他的UIKit類(除非另有說明,例如在後臺線程構建UIImage■當爲)。

關於您當前代碼的一個註釋:在使用它之前,您需要將weakSelf分配到強大的局部變量中。否則,您的條件可能會通過,但在您真正嘗試使用它之前,可能會刪除weakSelf。它看起來像

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ 
    UIImage *tmpImage = [[UIImage alloc] initWithCGImage:asset.defaultRepresentation.fullScreenImage]; 

    __strong __typeof__(weakSelf) strongSelf = weakSelf; 
    if ([strongSelf.scrollView viewWithTag:tagNumber] != nil){ 

     dispatch_async(dispatch_get_main_queue(), ^{ 
      __strong __typeof__(weakSelf) strongSelf = weakSelf; 
      if ([strongSelf.scrollView viewWithTag:tagNumber]!= nil){ 
       UIImageView * tmpImageView = (UIImageView*)[strongSelf.scrollView viewWithTag:tagNumber]; 
       tmpImageView.image = tmpImage; 
      } 
     }); 
    } 
}); 

從技術上講,你不必這樣做在後臺隊列中的第一個條件,因爲你只能提領一次出現,但它總是來存儲您的疲軟是個好主意當然,在接觸它之前變量變成一個強大的變量。

+0

弱。強大。我的頭在旋轉!我想我明白你的意思了。小後續:在主隊列調用的塊中,我添加了一個附加條件來檢查是否存在具有所需標記的視圖。後來,我認爲這是多餘的,因爲我早些時候檢查。這是錯誤的,因爲它是可能的(如果我正在做一些子視圖洗牌),當主隊列觸發時,它是**後**,並與背景隊列和第一個條件分開。 (新手線程用戶在這裏) –

1

如果您需要在UIImageView中渲染圖像,則需要在主線程中執行此操作。除非您在代碼中顯示的主隊列中執行它,否則它將不起作用。任何UI渲染都是如此。

if ([weakSelf.scrollView viewWithTag:tagNumber]!= nil){ 
    UIImageView * tmpImageView = (UIImageView*)[weakSelf.scrollView viewWithTag:tagNumber]; 
    tmpImageView.image = tmpImage; 
} 

Apple documentation

線程注意事項:操作,以應用程序的用戶界面必須在主線程上發生。因此,您應始終通過在應用程序的主線程 中運行的代碼來調用 UIView類的方法。唯一一次這可能不是絕對必要的 是創建視圖對象本身,但所有其他操作 應發生在主線程上。

1

是的,你需要使用主線程,因爲任何UI更改都需要在主線程中完成。

至於使用GCD,它被用來利用設備上的多核。 至於強弱自我

強烈的自我:你可能想在你的代碼強烈的自我,爲

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ 
(<your class> *) *strongSelf = weakSelf; 
    UIImage *tmpImage = [[UIImage alloc] initWithCGImage:asset.defaultRepresentation.fullScreenImage]; 

    if ([strongSelf.scrollView viewWithTag:tagNumber] != nil){ 

     dispatch_async(dispatch_get_main_queue(), ^{ 

      if ([strongSelf.scrollView viewWithTag:tagNumber]!= nil){ 
       UIImageView * tmpImageView = (UIImageView*)[strongSelf.scrollView viewWithTag:tagNumber]; 
       tmpImageView.image = tmpImage; 
      } 
     }); 
    } 
}); 

說你有一個觀點,使一個API調用,它需要一定的時間,所以你切換回另一個視圖,但你仍然希望圖像被下載,然後使用強,因爲該塊擁有自我,因此圖像被下載。

虛弱自我:如果在上述情況下,您不希望圖像下載後,您移動到不同的視圖,然後使用弱自我,因爲該塊沒有任何自我。

0

如果你不會在Kevin Ballard建議的代碼中使用strongSelf,那麼它可能會導致崩潰,因爲虛弱得不償失。

也是一個很好的做法是,即使在創建

strongSelf = weakSelf 

    if(strongSelf) 
    { 
     // do your stuff here 
    }