iOS文件路径中的/private前缀表示什么?

37

我的应用在iPhone上运行时出现错误,但在模拟器上运行时没有问题。我使用主目录路径的长度来提取/Documents文件夹中文件的相对路径。不幸的是,在iPhone上这种方法并不总是正确的,因为主目录路径前缀会添加 "/private"。然而,不管有无前缀,都可以正确引用同一个文件。以下代码演示了这种不一致性。iOS何时会提供 "/private" 前缀?

- (IBAction)testHomepath:(id)sender {
    NSFileManager *fmgr = [NSFileManager defaultManager];
    NSString  *homePath = [NSString stringWithFormat:@"%@/Documents",NSHomeDirectory()];
    NSString  *dirPath  = [homePath stringByAppendingPathComponent:@"TempDir"];
    NSURL     *dirURL   = [NSURL fileURLWithPath:dirPath];
    NSString  *filePath = [dirPath  stringByAppendingPathComponent:@"test.jpg"];
    [fmgr createDirectoryAtPath:dirPath withIntermediateDirectories:NO attributes:nil error:nil];
    [fmgr createFileAtPath:filePath contents:nil attributes:nil];
    NSArray *keys  = [[NSArray alloc] initWithObjects:NSURLNameKey,nil];
    NSArray *files = [fmgr contentsOfDirectoryAtURL:dirURL includingPropertiesForKeys:keys options:0 error:nil];
    NSURL *f1 = (files.count>0)? [files objectAtIndex:0] : 0;
    NSURL *f2 = (files.count>1)? [files objectAtIndex:1] : 0;
    bool   b0 = [fmgr fileExistsAtPath:filePath];
    bool   b1 = [fmgr fileExistsAtPath:f1.path];
    bool   b2 = [fmgr fileExistsAtPath:f2.path];

    NSLog(@"File exists=%d at path:%@",b0,filePath);
    NSLog(@"File exists=%d at path:%@",b1,f1.path);
    NSLog(@"File exists=%d at path:%@",b2,f2.path);
}
下面是在 iPhone 上运行时写入日志的内容。我手动添加了空格以显示第一行和第二行之间的差异。
2013-02-20 16:31:26.615 Test1[4059:907] File exists=1 at path:        /var/mobile/Applications/558B5D82-ACEB-457D-8A70-E6E00DB3A484/Documents/TempDir/test.jpg
2013-02-20 16:31:26.622 Test1[4059:907] File exists=1 at path:/private/var/mobile/Applications/558B5D82-ACEB-457D-8A70-E6E00DB3A484/Documents/TempDir/test.jpg
2013-02-20 16:31:26.628 Test1[4059:907] File exists=0 at path:(null)
以下是在模拟器上运行时(无“/ private”)写入日志的内容:
2013-02-20 16:50:38.730 Test1[7224:c07] File exists=1 at path:/Users/kenm/Library/Application Support/iPhone Simulator/6.1/Applications/C6FDE177-958C-4BF5-8770-A4D3FBD281F1/Documents/TempDir/test.jpg
2013-02-20 16:50:38.732 Test1[7224:c07] File exists=1 at path:/Users/kenm/Library/Application Support/iPhone Simulator/6.1/Applications/C6FDE177-958C-4BF5-8770-A4D3FBD281F1/Documents/TempDir/.DS_Store
2013-02-20 16:50:38.733 Test1[7224:c07] File exists=1 at path:/Users/kenm/Library/Application Support/iPhone Simulator/6.1/Applications/C6FDE177-958C-4BF5-8770-A4D3FBD281F1/Documents/TempDir/test.jpg

5
最糟糕的做法是对你的应用程序文档目录的路径作出任何假设。预期该路径的特定长度甚至更糟。只需要确定文档路径并从完整路径中删除它,以获取相对路径即可。 - rmaddy
@maddy,我并没有假设一个特定的长度,只是认为路径到/Documents不会改变,但这被IOS违反了,因为正如Kevin Ballard在下面指出的那样,/private只是一个符号链接。我来自Windows,从未见过这种情况。现在,我正在查找/NSHomeDirectory()/Documents中的子字符串,并在IOS给我的任何路径中调用该路径字符串之后的相对路径。您是否看到任何问题或知道更好的获取相对路径的方法? - KenM
3
你的问题陈述:「我正在使用家目录路径的长度」。你应该始终使用相对于「文稿」目录的路径。永远不要保留完整的路径。如果你只有相对路径,那就没有什么需要处理的了。 - rmaddy
1
@maddy,我只保留相对路径,但NSFileManager contentsOfDirectoryAtURL返回完整路径。我需要删除到/Documents的内容,以获取我可以保存的相对路径。 - KenM
1
请使用NSFileManager contentsOfDirectoryAtPath:error:代替。返回的路径列表是相对于您获取内容的路径的。 - rmaddy
感谢您的反馈,@maddy。DirectoryAtPath和DirectoryAtURL之间的区别很有用。我以为我需要缓存的PropertiesForKeys,但也许不需要。 - KenM
6个回答

33

我在调试器中尝试了这个方法,发现 URLByResolvingSymlinksInPath 可以“修复”添加了 /private/ 的路径。

(lldb) p (NSURL *)[NSURL fileURLWithPath:@"/private/var" isDirectory:YES]
(NSURL *) $1 = 0x1fd9fc20 @"file://localhost/private/var/"
(lldb) po [$1 URLByResolvingSymlinksInPath]
$2 = 0x1fda0190 file://localhost/var/

(lldb) p (NSURL *)[NSURL fileURLWithPath:@"/var" isDirectory:YES]
(NSURL *) $7 = 0x1fd9fee0 @"file://localhost/var/"
(lldb) po [$7 URLByResolvingSymlinksInPath]
$8 = 0x1fda2f50 file://localhost/var/

正如您所看到的,file://localhost/var 是我们真正想要的。

因此,很明显/private/var是指向/var的符号链接。 然而,@Kevin-Ballard指出这并不是真的。我确认他是正确的,/var是指向/private/var的符号链接(叹气)

(lldb) p (NSDictionary *)[[NSFileManager defaultManager] attributesOfItemAtPath:@"/var" error:nil]
(NSDictionary *) $3 = 0x1fda11b0 13 key/value pairs
(lldb) po $3
$3 = 0x1fda11b0 {
    ...
    NSFileType = NSFileTypeSymbolicLink;
}

(lldb) p (NSDictionary *)[[NSFileManager defaultManager]   attributesOfItemAtPath:@"/private/var" error:nil]
(NSDictionary *) $5 = 0x1fda4820 14 key/value pairs
(lldb) po $5
$5 = 0x1fda4820 {
    ...
    NSFileType = NSFileTypeDirectory;
}

所以URLByResolvingSymlinksInPath在这里做了一些有趣的事情,但现在我们知道了。对于这个特定的问题,URLByResolvingSymlinksInPath仍然听起来是一个很好的解决方案,适用于模拟器和设备,并且如果有什么变化,它应该继续工作。


2
在iOS上,“/var”实际上是指向“/private/var”的符号链接。可能“-URLByResolvingSymlinksInPath”会特殊处理这种情况,以提供更规范的路径。 - Lily Ballard
很棒的讨论;我在使用PDF阅读器加载文件时遇到了问题,而这一切都围绕着符号链接展开。一旦我找到了这个解释,解决方案就跃然纸上,问题也得以解决。 - Nathan B.
2
苹果文档指出,URLByResolvingSymlinksInPath 方法会移除路径中的 /private 前缀。https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/Reference/Reference.html#//apple_ref/doc/uid/20000301-SW32 - Jay Lieske
解决了我所有的路径问题。 - Fabiosoft

7
在Swift 3中,URL具有standardizedFileUrl属性,该属性将删除任何符号链接并解析路径中的相对部分,例如./。截至撰写本文时,文档(link1)非常无用,但它看起来等同于NSURL的standardized属性(link2)。

1
注意:URL有两个相似的属性 - standardized不起作用,但standardizedFileUrl可以使用! - Vyachaslav Gerchicov

5

/var 只是一个指向 /private/var 的符号链接。所以第一个路径是您尝试访问的逻辑路径,第二个路径则展开了符号链接。


谢谢提示,但似乎相反。请看我的回答。 - Skotch
@Skotch:实际上不是这样的。请看我对你回答的评论。 - Lily Ballard
在Mac OS X上,/var长时间以来一直是指向/private/var的符号链接。即使在iOS存在之前也是如此。 - Walt Sellers
contentsOfDirectory返回/private/var/...,通过使用resolvingSymlinksInPath()返回/var/...。 - mehdi

4

实际上回答你的问题:

我认为在发布OS X时添加了前缀/private(我不认为在NeXTStep中有它,但已经是数十年了)。它似乎存在以容纳etcvartmp(以及奇怪的tftpboot; 我不知道我的PBG4可以这样做),也许是因为用户不会想知道这个傻傻的名叫etc的文件夹并试图删除它。

在设备上,苹果决定将用户数据存储在/private/var/mobile中(用户名为“mobile”)。我不确定他们为什么没有选择/Users/mobile或只是/mobile,但它与在“正常”的Unix上的/var/mobile没有更多的意义。

在模拟器上,你的用户帐户不能写入/var(出于良好原因)。用户数据存储在~/Library/Application Support/iPhone Simulator某个地方。曾经有一段时间,他们开始为不同的模拟器版本使用不同的目录。


0
  1. 在我的情况下,使用FileManager.default.fileExists来检查文件是否已经存在,但我使用了.absoluteString/"()"而不是.path将URL作为字符串传递。
  2. 当我打印文档目录中文件的路径时,使用

let fileURLs = try fileManager.contentsOfDirectory(at:

它打印为/private/var

let DocumentDirURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)

它打印为/var

  1. 这种路径上的差异让我感到困惑。
  2. 如上所述,这只是一个符号链接。即两个路径是相同的。

:)


0
一个(Swift 5)从缓存中删除文件的函数。我遇到了很多问题,使用URL解决了私有问题。
class func removeFileFromCache( filePattern: String ) -> Bool {
    // find filename with pattern
     
    do {
        let cachePath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
        let filePathUrl = URL( fileURLWithPath: filePattern )
        let originalFilename = filePathUrl.lastPathComponent
        let newFilePath = cachePath.appendingPathComponent( originalFilename ).path
       
        try FileManager.default.removeItem(atPath: newFilePath )
        print("file \( filePattern ) succesfull removed.",classname:URL(fileURLWithPath: #file).lastPathComponent )
        
    } catch let error as NSError {
        print("Error \(error)",classname:URL(fileURLWithPath: #file).lastPathComponent )
        return false // all is not well
    }
    
    return true
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接