16

我是 GCD 和块的新手,我正在轻松进入它。

背景:我正在使用 ALAssetsLibrary 为 UIScrollView 开发延迟加载例程。当我的 UIScrollView 加载时,我用aspectRatioThumbnails我的 ALAsset 填充它,然后当用户滚动时,我调用下面的例程来加载fullScreenImage当前正在显示的 ALAsset。它似乎工作。

(如果有人有更好的延迟加载例程,请发表评论。我已经查看了我能找到的所有内容以及 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

4 回答 4

33

是的,当你触摸sUIImageView或任何其他 UIKit 类时,你需要使用主线程(除非另有说明,例如UIImage在后台线程上构造 s 时)。

关于您当前代码的一条评论:您需要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;
            }
        });
    }
});

从技术上讲,您不需要在后台队列的第一个条件中执行此操作,因为您只是在那里取消引用它一次,但是在触摸它之前将弱变量存储到强变量中总是一个好主意课程。

于 2013-01-17T00:37:29.967 回答
1

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

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

根据Apple 文档

线程注意事项:对应用程序用户界面的操作必须在主线程上进行。因此,您应该始终从应用程序主线程中运行的代码调用 UIView 类的方法。唯一可能不是绝对必要的情况是在创建视图对象本身时,但所有其他操作都应在主线程上进行。

于 2013-01-17T00:39:05.133 回答
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 调用的视图,这需要时间,所以您切换回另一个视图,但您仍然希望下载图像然后使用 strong ,因为该块拥有自身,因此下载了图像。

弱自我:如果在上述情况下您不想在移动到不同视图后下载图像,请使用弱自我,因为该块不拥有任何自我。

于 2013-01-17T10:01:20.560 回答
0

如果你不在 Kevin Ballard 建议的代码中使用 strongSelf,那么它可能会因为 weak 被取消而导致崩溃。

一个好的做法是在创建时甚至检查 strong 是否为非 nil

strongSelf = weakSelf

   if(strongSelf)
   {
       // do your stuff here 
   }
于 2014-01-10T15:53:33.090 回答