2013-01-17 19 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 
    }