UIImage imageNamed需要pathForResource吗?

joh*_*ers 15 iphone objective-c uiimage nsbundle

在创建使用时,使用该NSBundle方法搜索图像的路径有多必要?我看到教程代码只是直接指定图像的名称,然后代码更加努力地找到路径.pathForResourceUIImageimageNamed

根据我的经验,我一直只是直接使用这个名字而且它总是很好用.我以为它会自动知道如何找到图像.有多重要或在什么情况下需要做更多的事情?

Rhu*_*arb 60

完全没有 ...是原始问题的答案:

在使用imageNamed创建UIImage时,使用NSBundle方法pathForResource搜索图像的路径有多必要?

没有多少 ...... Zoul接受的答案和Ranga的另一个答案是多么正确.公平地说:如果您正在讨论应用程序包目录结构,或者图像位于Xcode中的"蓝色"文件夹中的(罕见)情况(稍后会详细介绍),它们是正确的,但不是最常见的常见病例

无论如何,对一个真正的答案.

像往常一样,我在试图找到答案时发现了这个问题.我从来没有发现这个问题的文档或其他答案令人满意,所以我决定测试.

我的测试详细信息都在下面,但让我在这里总结一下结果.简而言之,当使用imageNamed:加载图片时,它取决于你放置它们的位置:

  1. 如果您的图像位于项目的根目录中,即使组织在纯逻辑Xcode组中,那么不需要考虑路径:只需要图像名称.

  2. 如果您的图像位于附加到文件系统目录的组中,则通过"为添加的文件夹创建组",您仍然不必担心名称.

  3. 如果您的图像位于"蓝色"组中,通过"为添加的文件夹创建文件夹引用"附加到文件系统中的目录,则可以使用imageNamed加载它:通过指定建议的相对路径(巧合?)通过上面接受的答案.

  4. 如果您使用imageNamed:,imageWithContentsOfFile:的主要替代方法,您确实需要包含包路径的文件的完整路径,这意味着您需要知道Xcode导航器结构如何转换为包目录结构中的路径.

这两种方法之间的其他重要区别:

  • imageNamed不要求你指定文件类型扩展名,所以只是"icon"而不是"icon.png",而imageWithContentsOfFile确实需要完整的文件名
  • 第一点有助于第二个功能:imageNamed将 自动加载图像的视网膜版本(如果有),方法是将@ 2x添加到文件名中.因此,如果您要求"icon",在视网膜显示屏上将尝试加载"icon@2x.png".imageWithContentsOfFile没有
  • imageNamed缓存图像:其中存在很多争议:如果你搜索SO或网络,你会发现很多帖子建议你避免它,因为它没有正确清除它的缓存.然而,这是几年前修复的,因此您无需担心它无法清除其缓存.你仍然需要担心的事实,它缓存所有,但.如果你的图像很大并且不经常加载,你可以通过从文件中加载它们而不是缓存它们来节省内存.这与泄漏无关:即使您没有泄漏,设备上的内存仍然有限,并且您不希望不必要地进行缓存.这是经典的缓存权衡:在您的情况下哪些更重要?内存性能或CPU性能(时间).

所以我的测试.

我所做的是创建一个简单的UITableView应用程序,其中包含3个简单的图标文件,使用不同的方法在表格的行中显示.这些图标在Xcode项目结构中的位置不同.注意强调Xcode.理解原始问题答案的关键是iOS应用程序中有三个完全不同的项目目录结构:您在Xcode导航器中看到的那个,您在Finder中看到的同一项目的文件系统上的那个(右键单击Xco​​de导航器中的任何项目,然后选择"在Finder中显示"),以及您很少看到的项目,即已部署应用程序的"bundle"目录结构.您也可以在Finder中看到最后一个 - 通过在〜/ Library/Application Support/iPhone Simulator中找到您的应用程序,然后深入到.app目录.我会在一分钟内给你看一张我的照片.

所以在我的应用程序中,我将所有三个图标png图像文件以不同方式拖入Xcode:

  1. icon1.png(时钟),我拖着作为一个文件到Xcode项目的根目录,然后我后来在Xcode中创建一个新组,并把它拖成.该组不由文件系统中的任何目录表示:它是纯Xcode组.因此它的名字是:"JustGroup"

  2. icon2.png(一只眼睛),我最初将我的文件系统放在名为"RealDir"的目录中,然后将整个目录拖到Xcode中,当被问到时,我选择了"为任何添加的文件夹创建组"选项.这意味着Xcode中的RealDir组附加到文件系统(在我的项目目录中)中名为RealDir的真实目录,并且那里有icon2.png.

  3. icon3.png(一个目标),我也有一个单独的目录,我也拖进了Xcode.只有这次我选择了第二个单选"为任何添加的文件夹创建文件夹引用".这在Xcode中创建了一个所谓的"蓝色"组.更多关于以后的内容.我把这个组(和目录)称为"FolderReference"

以下是Xcode为您提供的选择: 拖动目录时的Xcode对话框

这就是我的项目结构在Xcode中的样子: Xcode导航器项目结构

现在,在我的应用程序中,我使用了两种方法来加载每个图标:UIImage imageNamed:和UIImage imageWithContentsOfFile.我在表中创建了一堆行,每个单元格的标题是包含图标的组的名称:JustGroup,RealDir或FolderReference,以及使用的方法的名称:imageNamed vs fromFile(我使用的是imageWithContentsOfFile的缩写)

单元格的详细标签(标题下的较暗文本)显示了我给该方法的文件或路径名称.

要清楚,在"fromFile"的情况下,我将捆绑路径添加到您看到的"相对"名称.所以对于"fromFile",我实际上是在使用这段代码:

NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
NSString *imagePath = [NSString stringWithFormat:@"%@/%@", bundlePath, filePath];
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
Run Code Online (Sandbox Code Playgroud)

其中"filePath"是您在表格单元格详细信息标签中看到的路径.另一方面,对于imageNamed:,逐字传递单元格详细信息中的filePath.

并且行图像自然是加载的图像.因此,对于表中没有图像的行,图像加载失败.

简而言之,就是结果.如果你对这篇文章一无所知,至少浏览一下这张图片会告诉你所有你需要知道的事情.

应用程序显示已加载的图标

以下是易于理解的基本解释:

  • 正如官方文档中所述,imageNamed:方法从应用程序包中加载图像.这意味着您不需要指定包位置,只需指定文件名.即便如此,只需要文件的基本名称.这里的文档有点薄,它应该清楚地表明它从给定的文件路径加载对于应用程序包根目录的图像.

  • (这里是踢球者,注意这个)关于bundle目录的规则,引用你部署的应用程序包中的根目录.如果你去探索,那就意味着在".app"目录中.那是不一样的在Xcode导航Xcode项目的根目录,也不是一样的取景器Xcode项目的根目录

  • 这是因为,在部署应用到设备(或仿真器)由"代表所有的项目目录时组进行添加的文件夹 "被夷为平地.也就是说,忽略该目录,并将其所有内容毫不客气地转储到捆绑包的根目录中.(我说"毫不客气地",因为如果在不同的文件夹中有相同名称的文件,它们将在这里发生碰撞,你将无法解决导致的问题.)在我的例子中,这就是RealDir的情况:部署的应用程序,RealDir不再存在,并且icon2.png留给一般人群(可怕).几乎不用说,纯粹的逻辑Xcode组"JustGroup"也被忽略了 - 它从来都不是真正的目录,只是Xcode用户的视觉辅助 - 而icon1.png也在bundle root中.

    • 这就是imageNamed:能够加载icon2的原因.

    • 还有为什么imageWithContentsOfFile无法在"RealDir/image2.png"中找到它:因为部署的应用程序中没有RealDir目录.

  • "蓝色文件夹",在另一方面,即由"表示的目录为添加的文件夹的文件夹的引用 ",实际上是保持在应用程序包的目录结构.这显然是蓝色文件夹的重点:它们为您提供了一种在已部署的应用程序中创建目录结构的方法.我不确定最初的存在理由,但是一个很好的用例是你有几个目录包含具有相同名称的资源文件的替代版本,你希望你的应用程序能够在运行时在它们之间切换通过更改目录.无论如何,我的FolderReference中的icon3.png仍保留在已部署应用程序的FolderReference目录中.

    • 这就是为什么imageNamed:用"icon3" 找不到它,但可以用"FolderReference/icon3"找到它
    • imageWithContentsOfFile也能够使用FolderReference找到它,但只有在附加时,请记住使用上面代码的完整包路径.(这里的主要区别是:imageNamed在这种情况下使用相对路径,imageWithContentsOfFile始终使用绝对路径).

为了澄清,这是我的文件夹结构:

您在上面看到了我的Xcode项目导航器结构,这里是它下面的文件系统目录: Finder Xcode项目目录结构

最后,也许最重要的是,部署的捆绑文件系统目录结构: 部署捆绑目录结构

注意:我在Mac上的这个位置找到了这个:你会在类似的位置找到你的位置 - 你可能需要搜索一下,找出哪个丑陋的GUID命名的子目录包含你的应用程序.

 ~/Library/Application Support/iPhone Simulator/5.1/Applications/4EB386B2-CD7E-4590-9757-18DDDEE6AF4F/ImageLoadingTest.app 
Run Code Online (Sandbox Code Playgroud)

我希望这有帮助.测试,探索,最后描述它确实对我有所帮助.


zou*_*oul 5

文档说"该方法在应用程序的主包中查找具有指定名称的图像",所以我说你总是可以只使用名称.唯一的例外可能是存储在子文件夹中的图像,尤其是当你拥有foo/image.pngbar/image.png.我不知道是否[UIImage imageNamed:@"foo/image"]会奏效,但尝试是微不足道的.

(在这些情况下有点令人困惑的是,Xcode树中的组与生成的应用程序包中的文件夹不对应.它们的内容被一起粉碎到包的根目录,除非您使用蓝色文件夹引用而不是常规组.)

  • 实际上你是正确的,它确实有效,但是(并且它很大但是)_only_如果"foo"是你的包中的目录,那么Xcode中的"蓝色"文件夹.在最常见的情况下,您根本不需要"foo".(程序问题:你不应该回答,如果你只有文件和猜测继续下去,你也不应该接受答案,如果它没有解决你的问题 - 如果你有一个蓝色文件夹,那么一切都很好如果没有,那么这对你没有用.)请参阅下面关于这个主题的冗长的论文 (2认同)