我什么时候应该使用 NSURLCanonicalPathKey?

When should I be using NSURLCanonicalPathKey?

在 macOS 10.12 中,NSURLCanonicalPathKey 添加到 NSURL。文档指出:

The URL's path as a canonical absolute file system path.

除此之外,我唯一看到的其他 documentation/information 来自 a Swift Forum post,它指出:

You might want to take a look at .canonicalPathKey (NSURLCanonicalPathKey). On Apple platforms a lot of the standard UNIXy paths exist within /private/, with corresponding symlinks from the root. So /etc/ is actually /private/etc/. If you don’t canonicalise the paths you can get tripped up by this.

这对我来说似乎很重要,但令我惊讶的是它仅在 10.12 中引入。我只依赖 NSURLPathKey.path 或书签数据来解析 URL,从来没有遇到过问题

这是在 macOS 10.14 上,我只考虑指向文件或文件夹的 URL。我知道书签数据可能应该存储在数据库中而不是路径中。

这取决于您打算如何使用路径:

  • 如果您只想向用户显示路径,或存储它以供以后使用 [NSURL fileURLWithPath:] 重新创建 URL,那么您可以继续使用收到的常规路径它,因为通常你得到路径是因为用户以某种方式给了你,然后最好不要改变它。
  • 重新创建 URL 当然可以使用任一路径表示。但是,如果您从“/etc”创建一个 URL 并从“/private/etc”创建一个,[NSURL isEqual:] 将给您 false - 如果您不喜欢那样,您将必须将它们规范化。
  • 因此,如果您想注册路径以便稍后重新识别给您的相同路径,那么您应该将其规范化。
  • 请记住,获取规范路径会增加大量处理时间(很容易使处理时间加倍)。这就是为什么你要避免在没有必要的情况下使用它。

Unicode normalization 也可能很重要。例如,如果文件或文件夹使用预组合 (NFC) 字符,NSURL 方法会将它们转换为 NFD 字符串。 OTOH,BSD/POSIX 函数不会那样做。因此,例如,如果您从 shell 命令获取路径,然后将它们与您从 NSURLs 获得的路径进行比较,它们可能不会计算为相等,因为一个使用 NFC 而另一个使用 NFC NFD 字符。理想情况下,如果 NSURL 或 NSFileManager 涉及路径,那么您还应该首先通过 NSURL 传递您的 BSD 路径,以便最终以相同的组合格式获得两种类型的路径。

例子

不涉及规范化的简单示例:

Input URLByStandardizingPath NSURLCanonicalPathKey
/private/var /var /private/var
/var /var /private/var

Unicode 规范化

以下示例使用准备好的 APFS 卷,其中包含的文件名具有字母“ü”的预组合和分解表示,以及符号链接。你可以 download the disk image file here.

目录布局如下:

$ cd /Volumes/Canonical_Normalize_Test/
$ ls -lR
total 24
-rw-r--r--  1 user  staff   19 Dec 29 19:27 decomposed_ü
-rw-r--r--  1 user  staff   19 Dec 29 19:27 precomposed_ü
drwxr-xr-x  4 user  staff  128 Dec 29 19:36 symlink_target_dir
lrwxr-xr-x  1 user  staff   18 Dec 29 19:36 symlink_to_dir -> symlink_target_dir
-rwxr-xr-x@ 1 user  staff  763 Dec 15 16:28 unicode_composition_check.sh

./symlink_target_dir:
total 0
lrwxr-xr-x  1 user  staff  17 Dec 29 19:36 decomposed_ü -> ../decomposed_ü
lrwxr-xr-x  1 user  staff  17 Dec 29 19:36 precomposed_ü -> ../precomposed_ü

文件“unicode_composition_check.sh”是创建两个“...ü”文件的脚本,一个使用 NFD 命名,另一个使用 NFC(不幸的是,脚本命名不当)。

输入为:

/Volumes/Canonical_Normalize_Test/symlink_to_dir/precomposed_\U00fc

(即路径包括目录符号链接并使用实际文件的unicode组成,即目标文件名的“ü”是预先组成的。)

Method Result
fileSystemRepresentation /Volumes/Canonical_Normalize_Test/symlink_to_dir/precomposed_u\U0308
URLByStandardizingPath /Volumes/Canonical_Normalize_Test/symlink_to_dir/precomposed_u\U0308
NSURLCanonicalPathKey /Volumes/Canonical_Normalize_Test/symlink_target_dir/precomposed_u\U0308
URLByResolvingSymlinksInPath /Volumes/Canonical_Normalize_Test/precomposed_u\U0308

我们看到每种方法给出不同的结果:

  1. 它们似乎都将路径规范化为 NFD,即“ü”在所有情况下都会分解。对于不区分大小写的常规卷来说,这是必要且正常的,因为文件名的 lookup 是规范化不敏感的。但是:对于区分大小写的卷,不得更改组成,虽然我没有对此进行测试,但我假设上述所有函数都会检测卷的区分大小写模式并相应地运行。

  2. 只有 NSURLCanonicalPathKey 给出正确的结果,如果我们想稍后通过路径重新识别目标项目(不管使用哪种 Unicode 组合以及路径是否包含符号链接)到目录):它解析目录符号链接,但 不是 symlink_target_dir 内的最终符号链接。如果它确实解析了最终路径元素(如 URLByResolvingSymlinksInPath 那样),您将无法定位符号链接文件。

  3. NSString 的 fileSystemRepresentation 不会改变路径(但对其进行规范化),而 NSURL 的 URLByStandardizingPath 在某些情况下会改变路径(例如通过删除来自某些根文件夹的“/private”。

  4. 只有 NSURLCanonicalPathKey 会根据实际的磁盘路径修复 upper/lower 的情况。例如,从“/applications”创建的 URL 不会被任何其他函数转换为实际的“/Applications”路径。

结论

如果您以后需要重新识别路径,无论使用哪种表示(规范化、指向目录的符号链接),如果您需要保留实际项目,请使用 NSURLCanonicalPathKey,即使它是符号链接,或使用 URLByResolvingSymlinksInPath 始终识别提供给您的任何符号链接的目标。

注意,但是(见第一个例子)如果你使用URLByResolvingSymlinksInPath,“/private/var/tmp”等将变成“/var/tmp”等,这是不常用的,因为然后它仍然包含一个符号链接(即“/var”)。

另请记住,除非您获得规范路径,否则大小写可能不正确。为了弥补这一点,比较路径需要您首先检查路径是否在不区分大小写的卷上,以便您使用正确的比较选项(并且,作为一个额外的复杂化,只需将路径与“不区分大小写”的卷进行比较对于 HFS+ 卷上的某些稀有脚本,该选项可能不正确,因为它们使用较旧的 Unicode 标准,该标准具有一些其他规则而不是当前 macOS 版本使用的规则)。

最后,如果你只是想看看两个路径是否指向同一个文件,使用其他不依赖路径的方式更安全。请参阅 this answer. And if you need to persistently remember file locations, it's best to use bookmarks,这样即使用户在此期间重命名或移动了文件,也能找到它们。


免责声明:所有这些发现都是根据经验发现的,并在 macOS 10.13.6 和 11.1(以及介于两者之间的系统)上进行了测试,因此您可能需要仔细检查我的发现并发表评论如果你得到不同的结果。