GCD - 用于更新UIImageView的主要vs后台线程

时间:2013-01-17 00:27:45

标签: ios grand-central-dispatch

我是GCD的新手并阻止我进入它。

后台:我正在使用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;
                }
            });
        }
    });
}

4 个答案:

答案 0 :(得分:33)

是的,你需要在触摸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;
            }
        });
    }
});

从技术上讲,您不需要在后台队列中的第一个条件中执行此操作,因为您只是在那里取消引用它,但在将其作为强变量存储之前将其保存为强变量始终是个好主意。理所当然。

答案 1 :(得分:1)

如果需要在UIImageView中渲染图像,则需要在主线程中执行此操作。除非您在代码中显示的主队列中执行此操作,否则它将无法工作。任何UI渲染的情况都是如此。

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

根据Apple documentation

  

线程注意事项:应用程序用户界面的操作必须在主线程上进行。因此,你应该经常打电话   从主线程中运行的代码中获取UIView类的方法   你的申请。这可能不是绝对必要的唯一时间   是在创建视图对象本身但是所有其他操作时   应该发生在主线程上。

答案 2 :(得分: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调用的视图并且需要时间,因此您切换回另一个视图但仍然希望下载图像然后使用强,因为该块拥有自己以便下载图像。 / p>

弱自我:如果在上述情况下你不希望图像下载一旦你移动到另一个视图然后使用弱自我,因为该块不拥有任何自我。

答案 3 :(得分:0)

如果你不会在Kevin Ballard建议的代码中使用strongSelf,那么它可能会导致崩溃,因为它很难被缩小。

另一个好的做法是在创建

时检查强大的非零
strongSelf = weakSelf

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