简体   繁体   English

从资产目录中加载图像到主线程之外?

[英]Load an image from an asset catalog off the main thread?

I'm trying to load an image from an asset catalog into a UIImage off of the main thread, but can't seem to find any way to do it. 我正在尝试将资产目录中的图像从主线程加载到UIImage中,但似乎找不到任何方法。 If the image was not in an asset catalog I would use pathForResource and imageWithContentsOfFile: 如果图像不在资产目录中,我将使用pathForResource和imageWithContentsOfFile:

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

But pathForResource: does not work for images in asset catalogs. 但是pathForResource:不适用于资产目录中的图像。 Is there any way to do this without having to go to the main thread to load the image? 有什么方法可以执行此操作而不必转到主线程来加载图像吗? Im guessing the answer is no. 我猜答案是否定的。

What won't work: 什么不起作用:

  • Manually creating the path for the image based on the main bundle path. 根据主捆绑包路径手动创建图像的路径。 I have images in multiple asset catalogs, and asset catalogs can be compiled in a binary form. 我在多个资产目录中都有图像,并且资产目录可以以二进制形式进行编译。
  • [UIImage imageNamed:] is not thread safe, and can not be used off the main thread. [UIImage imageNamed:]不是线程安全的,并且不能在主线程之外使用。 The docs have said it's not thread safe for at least the last several major releases of iOS, and starting in iOS 8 it really does crash when used off the main thread. 文档已经说过,至少对于iOS的最后几个主要版本来说,它不是线程安全的,并且从iOS 8开始,当关闭主线程时,它确实崩溃了。

What i'm doing now: 我现在在做什么:

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

It seems like you've answered your own question: use dispatch_sync to invoke imageNamed: on the main thread. 看来您已经回答了自己的问题:使用dispatch_sync在主线程上调用imageNamed:

The Asset Catalog format is opaque; 资产目录格式不透明; your only interface to it is imageNamed: . 您唯一的接口是imageNamed: Its documentation says: 它的文档说:

This method looks in the system caches for an image object with the specified name and returns that object if it exists. 此方法在系统缓存中查找具有指定名称的图像对象,并返回该对象(如果存在)。 If a matching image object is not already in the cache, this method locates and loads the image data from disk or asset catalog, and then returns the resulting object. 如果高速缓存中还没有匹配的图像对象,则此方法从磁盘或资产目录中查找并加载图像数据,然后返回结果对象。 You can not assume that this method is thread safe. 您不能假定此方法是线程安全的。

There is a trade-off here between speed and safety; 在速度和安全性之间需要权衡。 Apple chose speed. 苹果选择了速度。 To allow imageNamed: to be called from any thread would require either (A) synchronizing access to the system caches, adding overhead or (B) not using a cache at all. 要允许从任何线程调用imageNamed:将需要(A)同步访问系统缓存,增加开销,或者(B)完全不使用缓存。

Since imageNamed: is most often used to support loading of images for the UI, and the UI work is already tied to the main thread, it makes sense to optimize around that case, and push the work of synchronization onto callers who are not on the main thread. 由于imageNamed:最常用于支持UI的图像加载,并且UI工作已经与主线程绑定,因此有必要针对这种情况进行优化,并将同步工作推到不在主线程上的调用者上。主线程。

So, using dispatch_sync is the way to go. 因此,使用dispatch_syncdispatch_sync的方法。 If you are worried about thread contention, you could easily cache the images so that the background thread only waits on the main thread when it absolutely has to. 如果您担心线程争用,则可以轻松地缓存图像,以便后台线程仅在绝对必要时才等待主线程。 I wouldn't do that unless you have determined via profiling that it would make a noticeable difference in performance, though. 但是,除非您通过性能分析确定它将对性能产生显着影响,否则我不会这样做。

Note that if Apple had decided to allow imageNamed: to be called on a background thread, they would have had to synchronize access to their caches, making the performance not that much different than adding dispatch_sync to your own code. 请注意,如果苹果决定允许imageNamed:对后台线程中调用,他们将不得不同步访问他们的高速缓存,使得性能没有太大比增加不同dispatch_sync自己的代码。

If your requirements are: 如果您的要求是:

  • you must absolutely, positively, never touch the main thread; 您必须绝对,绝对不要触碰主线程; and
  • you must absolutely, positively, include your images in an Asset Catalog; 您必须绝对肯定地将您的图像包括在资产目录中;

then the only alternative that I can see is reverse engineering the Asset Catalog file format and reading from the disk directly. 那么我可以看到的唯一替代方法是对资产目录文件格式进行反向工程并直接从磁盘读取。 Your app may break if the file format changes, but fortunately that would only happen at build time, so future OS updates shouldn't break your app. 如果文件格式更改,您的应用程序可能会中断,但幸运的是,这只会在构建时发生,因此将来的OS更新不会破坏您的应用程序。

If you're loading an image saved in the project you'd use [UIImage imageNamed: @"image1"]; 如果要加载保存在项目中的图像,请使用[UIImage imageNamed: @"image1"]; no extensions like @"image1.png" 没有扩展名,例如@“ image1.png”

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

Hope this helps :) 希望这可以帮助 :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM