是否可以创建iOS应用程序包中文件的硬链接?

时间:2014-04-11 15:32:52

标签: ios iphone symlink nsfilemanager hardlink

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

虽然我已经能够使用.../My Custom Subfolder/ API NSFileManagercreateSymbolicLinkAtURL:withDestinationURL:error:中成功创建包中文件的符号链接,但我会使用一些框架API来访问稍后的内容得到了充实,并返回了与符号链接有关的属性和数据,而不是基础文件。我可以通过利用其他一些框架API来缓解这些问题,但根据不正确的用法范围,它可能最终会有很多工作。

在模拟器上,我通过使用NSFileManager API linkItemAtURL:toURL:error:创建包内容的硬链接,成功解决了这个问题。硬链接适用于整个应用程序中使用的所有文件访问API,一切都很好。然而,在DEVICE上(在运行iOS 7.0的iPhone 5c和运行iOS 7.1的iPad上测试过),我会收到NSCocoaErrorDomain 513 error (Operation could not be completed. Operation not permitted.)。我可以在.../My Custom Subfolder/中创建一个测试文件并在同一个文件夹中创建一个硬链接就好了,但是如果我尝试硬连接到只读应用程序包中的任何内容,我会收到513错误。

有没有人知道是否有办法解决权限错误以完成我正在尝试做的事情?

1 个答案:

答案 0 :(得分:4)

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

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

  • 如果可写dir中存在相对路径,则返回此路径。这应该是所请求的相对文件路径的更新版本
  • 如果上一次检查失败,请在主捆绑目录中查找以从应用程序的主捆绑包中返回原始版本

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

#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")而不是查看主包。它也适用于refrenced文件夹(拖放文件夹到xcode并选择创建引用而不是组)。然后,您可以引用父文件夹的相对路径(如advertisements / adbanner.png)。

唯一的缺点是,如果您有Web内容,则需要更新html网站引用的所有文件,原因显而易见(Web浏览器只能跟踪html文档所在文件夹的相对路径)。