从主线程的资产目录中加载图像?

Posted

技术标签:

【中文标题】从主线程的资产目录中加载图像?【英文标题】:Load an image from an asset catalog off the main thread? 【发布时间】:2015-07-31 20:26:23 【问题描述】:

我正在尝试将资产目录中的图像加载到主线程之外的 UIImage 中,但似乎找不到任何方法。 如果图像不在资产目录中,我将使用 pathForResource 和 imageWithContentsOfFile:

NSString *path = [[NSBundle mainBundle] pathForResource:icon ofType:nil];
if(path) 
  image = [UIImage imageWithContentsOfFile:path];

但 pathForResource: 不适用于资产目录中的图像。 有什么方法可以做到这一点而不必去主线程加载图像?我猜答案是否定的。

什么不起作用:

根据主包路径手动创建图像路径。我在多个资产目录中有图片,资产目录可以编译成二进制形式。 [UIImage imageNamed:] 不是线程安全的,不能在主线程之外使用。文档说至少在 ios 的最后几个主要版本中它不是线程安全的,从 iOS 8 开始,在主线程外使用时确实会崩溃。

我现在在做什么:

dispatch_sync(dispatch_get_main_queue(), ^
  image = [UIImage imageNamed:icon];
);

【问题讨论】:

【参考方案1】:

您似乎已经回答了自己的问题:在主线程上使用dispatch_sync 调用imageNamed:

资产目录格式不透明;你唯一的接口是imageNamed:。它的文档说:

此方法在系统缓存中查找具有指定名称的图像对象,如果该对象存在,则返回该对象。如果缓存中没有匹配的图像对象,则此方法从磁盘或资产目录中定位并加载图像数据,然后返回结果对象。你不能假设这个方法是线程安全的。

这里需要在速度和安全性之间进行权衡;苹果选择了速度。要允许从任何线程调用 imageNamed:,需要 (A) 同步访问系统缓存,增加开销或 (B) 根本不使用缓存。

由于imageNamed: 最常用于支持 UI 的图像加载,并且 UI 工作已经绑定到主线程,因此围绕这种情况进行优化是有意义的,并将同步工作推送给调用者不在主线程上。

所以,使用dispatch_sync 是可行的方法。如果您担心线程争用,您可以轻松地缓存图像,以便后台线程仅在绝对需要时才在主线程上等待。不过,除非您通过分析确定它会对性能产生显着影响,否则我不会这样做。

请注意,如果 Apple 决定允许在后台线程上调用 imageNamed:,他们将不得不同步对其缓存的访问,这使得性能与添加 @ 没有太大差异 987654328@ 到您自己的代码。

如果您的要求是:

你必须绝对,绝对,永远不要接触主线程;和 您必须绝对、肯定地将您的图片包含在资产目录中;

那么我能看到的唯一替代方法是对资产目录文件格式进行逆向工程并直接从磁盘读取。如果文件格式发生变化,您的应用可能会中断,但幸运的是,这只会在构建时发生,因此未来的操作系统更新不应破坏您的应用。

【讨论】:

我认为我没有回答我自己的问题。除了去主线程之外,我一直在寻找解决方案。 @JesseCrocker 我认为不存在这样的解决方案。我扩展了我的答案,希望对您有所帮助。【参考方案2】:

如果您正在加载保存在项目中的图像,您将使用 [UIImage imageNamed: @"image1"]; 没有像 @"image1.png" 这样的扩展名

UIImage *image = [UIImage imageNamed: @"<#image name#>"];

希望这会有所帮助:)

【讨论】:

感谢您的回答,但您实际上并没有回答我提出的问题。 另外,你的回答不正确,[UIImage imageNamed: @"icon"] 和 [UIImage imageNamed: @"icon.png"] 都可以。 它停止为我工作,例如 icon.png、icon@2x.png 和 icon@3x.png

以上是关于从主线程的资产目录中加载图像?的主要内容,如果未能解决你的问题,请参考以下文章

从主线程中删除 AlamofireImage responseImage

UIWebView 中的本地 html,带有来自资产目录的图像

在主线程和工作线程中加载动态库(内部调用COM dll)有什么区别?

在 jFrame 中加载带有图像的面板:组件必须为非空

Volatile理解

Picasso java.lang.IllegalStateException:方法调用不应该从主线程发生