【问题标题】:Is it possible to create hard links to files in an iOS application bundle?是否可以在 iOS 应用程序包中创建指向文件的硬链接?
【发布时间】:2014-05-25 20:40:14
【问题描述】:

为了节省与我的 iOS 应用程序相关的服务器端带宽成本,我将一堆资产打包到我的 iOS 应用程序包中,否则这些资产可以在运行时下载。在编写的应用程序上下文中,如果我可以从用户可写目录之一(例如[App Dir]/Library/Application Support/My Custom Subfolder/)访问文件,而不必在运行时直接复制文件(例如在启动时,首先跑,随便)。

虽然我已经能够使用 NSFileManager API createSymbolicLinkAtURL:withDestinationURL:error:.../My Custom Subfolder/ 中成功创建指向捆绑包中文件的符号链接,但我随后用来访问内容的一些框架 API 却被搞砸了向上并给我与符号链接而不是基础文件有关的属性和数据。我可能可以通过使用其他一些框架 API 来缓解这些问题,但最终可能需要大量工作,具体取决于不正确使用的范围。

在模拟器上,我能够通过使用NSFileManager API linkItemAtURL:toURL:error: 创建指向捆绑内容的硬链接来成功规避此问题。硬链接对于整个应用程序中使用的所有文件访问 API 都非常有效,而且一切都很顺利。然而,在 DEVICE 上(在运行 iOS 7.0.2 的 iPhone 5c 和运行 iOS 7.1 的 iPad 上测试),我会收到 NSCocoaErrorDomain 513 error (Operation could not be completed. Operation not permitted.)。我可以在.../My Custom Subfolder/ 中创建一个测试文件并在同一个文件夹中创建一个硬链接,但如果我尝试硬链接到只读应用程序包中的任何内容,我会收到 513 错误。

有谁知道是否有办法绕过权限错误以完成我想要做的事情?

【问题讨论】:

  • @danh,感谢您的回复。但是,您的代码 sn-p 只是将文件复制到用户可写目录中,有效地使资源在磁盘上占用的空间量增加了一倍。我的意图是避免复制文件,而是使用“硬链接”指向文件,该文件的行为类似于文件,而不会消耗额外的磁盘空间。
  • 为什么需要硬链接到应用程序包中的文件而不是复制它们?听起来您正试图绕过修改应用程序包的禁令。如果允许这样做,就会造成巨大的安全漏洞。
  • @WilliamShakespeare 在我的情况下,我想使用硬链接而不是从捆绑包中复制文件,这更慢并且使用额外的磁盘空间。我不想修改包中的任何文件;只读没问题。我会使用符号链接(顺便说一句,我可以很好地创建它),但我们在通过符号链接播放视频时发现了错误。
  • @Michael Melanson 我错过了什么吗?如果您不打算修改文件,为什么不直接从应用程序包中使用它们?
  • @HotLicks 也许我会。但请记住,我也会将赏金奖励给提供“关于为什么这是不可能的权威解释”的人。

标签: ios iphone symlink nsfilemanager hardlink


【解决方案1】:

你根本无法绕过这个错误,我很确定。我没有找到任何官方文档 - 但有几个教程解释了如何允许在越狱设备上使用符号链接。我没有理由为什么 iOS 上的安全性与硬链接有任何不同。在模拟器中这是有效的,因为它在本地机器上的路径上运行(在 ~/Library/Application Support/iphone Simulator/iOSVersion/Applications/...)并且具有本地安全性。作为一个反例,我不止一次想到我在模拟器中工作的东西在设备上不起作用,反之亦然(例如,当尝试在主包中打开带有 SQLITE_WRITE 标志的 sqlite3 db 时) .

我通过使用两个不同的文件位置解决了我的应用程序中的这个资源管理/版本控制问题。原始版本像往常一样位于主包中。另一个位置是缓存目录或文档目录。然后我使用一个自定义资源处理程序,它返回一个提供的相对路径的完整路径。它的工作原理是这样的:

  • 如果可写目录中存在相对路径,则返回该路径。这应该是请求的相对文件路径的更新版本
  • 如果之前的检查失败,请在主包目录中查找以从应用的主包中返回原始版本

一些代码(使用缓存目录作为可更新目录,您可能需要更改):

#define UpdateableCacheResourcePath(path) \
[[ResourceHandler sharedManager] updateableCacheResourcePath:path]

- (NSString*)updateableCacheResourcePath:(NSString *)path
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory , NSUserDomainMask, YES);
    NSString *_libraryCacheStorageDir = [[paths objectAtIndex:0] copy];
    NSString *updateablePath = [_libraryCacheStorageDir stringByAppendingPathComponent:path];
    // Cache Dir
    if ([_fileManager fileExistsAtPath:updateablePath]) {
        return updateablePath;
    }

    // mainBundle
    NSString *mainbundleResourcePath = [_mainBundlePath stringByAppendingPathComponent:path];
    if ([_fileManager fileExistsAtPath:mainbundleResourcePath]) {
        return mainbundleResourcePath;
    }

    NSLog(@"File not found: %@", path);
    return nil;
}

在您的代码中,您将使用 UpdateableCacheResourcePath("some.file") 而不是查看主包。它也适用于引用文件夹(将文件夹拖放到 xcode 并选择创建引用而不是组)。然后您可以引用父文件夹的相对路径(如 ads/adbanner.png)。

唯一的缺点是,如果您有 Web 内容,您需要更新所有被您的 html 站点引用的文件,原因很明显(Web 浏览器只能遵循指向 html 文档所在文件夹的相对路径)。

【讨论】:

  • 感谢您的回答,但不幸的是它不满足赏金标准,因为没有解决方案,也没有解释为什么我不能创建硬链接。符号链接在相同的设置中肯定可以工作(请参阅问题的第 4 条评论),但我不能使用它们,因为通过符号链接使用时视频播放中存在错误。我同意没有明显的原因为什么硬链接不应该也起作用,这就是我发布赏金的原因。
  • 感谢 Michael Melanson 提供了一些激励措施来激发对该主题的兴趣。 @benjist,感谢您的建议。几个月前我提出这个问题时选择的解决方法与您在此处提出的建议非常相似。为了方便可行的解决方案,我让步并付出了硬盘占用翻倍的代价。由于需要在应用程序的整个生命周期内下载同一类别的其他资产,因此无论如何我都需要构建自己的资源管理系统(阅读:自定义有界缓存)。不理想,但它已经完成了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-19
  • 1970-01-01
  • 1970-01-01
  • 2013-02-10
  • 1970-01-01
  • 2018-07-09
  • 1970-01-01
相关资源
最近更新 更多