ios GCD - 用于更新 UIImageView 的主线程与后台线程

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14370325/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 21:52:52  来源:igfitidea点击:

GCD - main vs background thread for updating a UIImageView

iosgrand-central-dispatch

提问by spring

I'm new to GCD and blocks and am easing my way into it.

我是 GCD 的新手并且会阻止并且正在轻松进入它。

Background:I'm working on a lazy loading routine for a UIScrollView using the ALAssetsLibrary. When my UIScrollView loads I populate it with the aspectRatioThumbnailsof my ALAssets and then as the user scrolls, I call the routine below to load the fullScreenImageof the ALAsset that is currently being displayed. It seems to work.

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

(if anyone has a better lazy loading routine please post a comment. I've looked at all I could find plus the WWDC video but they seem to deal more with tiling or have much more complexity than I need)

(如果有人有更好的延迟加载例程,请发表评论。我已经查看了我能找到的所有内容以及 WWDC 视频,但他们似乎处理平铺的更多内容或比我需要的复杂得多)

My question:I use a background thread to handle loading the fullScreenImageand when that is done I use the main thread to apply it to the UIImageView. Do I need to use the main thread?I've seen that all UIKit updates need to happen on the main thread but I am not sure if that applies to a UIImageView. I was thinking it does, since it is a screen element but then I realized that I simply didn't know.

我的问题:我使用后台线程来处理加载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;
                }
            });
        }
    });
}

回答by Lily Ballard

Yes, you need to use the main thread whenever you're touching UIImageView, or any other UIKit class (unless otherwise noted, such as when constructing UIImages on background threads).

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

One commentary about your current code: you need to assign weakSelfinto a strong local variable before using it. Otherwise your conditional could pass, but then weakSelfcould be nilled out before you actually try to use it. It would look something like

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

Technically you don't need to do this in the first conditional in the background queue, because you're only dereferencing it once there, but it's always a good idea to store your weak variable into a strong variable before touching it as a matter of course.

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

回答by iDev

If you need to render the image in UIImageView, you need to do this in main thread. It will not work unless you do it in main queue as shown in your code. Same is the case with any UI rendering.

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

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

As per Apple documentation,

根据Apple 文档

Threading Considerations:Manipulations to your application's user interface must occur on the main thread. Thus, you should always call the methods of the UIView class from code running in the main thread of your application. The only time this may not be strictly necessary is when creating the view object itself but all other manipulations should occur on the main thread.

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

回答by MaheshShanbhag

Yes you need to use the main thread, since any UI changes needs to be done in the main thread.

是的,您需要使用主线程,因为任何 UI 更改都需要在主线程中完成。

As for using the GCD it is used to take the advantage of the Multi Cores on the device. As for the strong and weak self

至于使用 GCD,它用于利用设备上的多核。至于强弱的自己

strong self: you might want a strong self, for in you code

强大的自我:你可能想要一个强大的自我,因为在你的代码中

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;
            }
        });
    }
});

say you have a view which make a API call and it takes time, so you switch back to another view but you still want the image to be downloaded then use strong since the block owns the self so the image is downloaded.

假设您有一个进行 API 调用的视图,这需要时间,因此您切换回另一个视图,但您仍然希望下载图像,然后使用 strong,因为该块拥有 self,因此下载图像。

weak self: if in the above situation you dont want the image to download once you move to a different view then use weak self, since the block doesn't own any self.

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

回答by Shilpi

If you will not use strongSelf in the code suggested by Kevin Ballard then it might lead to a crash because of weak getting nilled out.

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

Also a good practice would be to even check for strong being non nil at the point of creating

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

strongSelf = weakSelf

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