В macOS 10.12 NSURLCanonicalPathKey был добавлен к NSURL. В документации указано:
The URL's path as a canonical absolute file system path.
Помимо этого, единственная другая документация/информация, которую я видел, исходит от пост на форуме Swift, в которой говорится:
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-адресов и никогда не было проблем.
Должен ли я теперь использовать канонический путь везде, где я раньше использовал стандартное значение пути?
Если я храню информацию о пути в базе данных в виде строки, должен ли я
сохранить значение .path или NSURLCanonicalPathKey?
Если я преобразовываю NSURL в строковое представление для использования в библиотеке C/C++, для которой требуется путь к файлу, должен ли я использовать каноническое представление пути?
Если вы показываете путь к файлу пользователю, должны ли вы показывать канонический путь?
Как NSURLCanonicalPathKey соотносится с URLByStandardizingPath и URLByResolvingSymlinksInPath, которые вроде как делают то же самое или противоположное...(?)
Это в macOS 10.14, и я рассматриваю только URL-адреса, указывающие на файлы или папки. Я знаю, что данные закладок, вероятно, должны храниться в базе данных, а не в путях.

Это зависит от того, как вы планируете использовать путь:
[NSURL fileURLWithPath:], вы можете продолжать использовать обычный путь в том виде, в каком вы его получили, потому что обычно вы получаете пути, потому что пользователь дал их вам каким-то образом, и тогда будет лучше, если вы его не переделываете.[NSURL isEqual:] даст вам false - если вам это не нравится, вам придется их канонизировать.Нормализация Юникода также может иметь значение. Например, если в файле или папке используются предварительно составленные (NFC) символы, методы NSURL преобразуют их в строки NFD. OTOH, функции BSD/POSIX этого не сделают. Таким образом, если вы, например, получаете пути из команды оболочки, а затем сравниваете их с путями, которые у вас есть из NSURL, они могут не считаться равными из-за того, что один использует символы NFC, а другой NFD. В идеале, если с путями связаны NSURL или NSFileManager, вам также следует сначала передать пути BSD через NSURL, чтобы в конечном итоге вы получили пути обоих типов в одном и том же формате композиции.
| Вход | URLByStandardizingPath | NSURLCanonicalPathKey |
|---|---|---|
| /частный/вар | /вар | /частный/вар |
| /вар | /вар | /частный/вар |
В следующем примере используется подготовленный том APFS, который содержит имена файлов как с предварительно составленным, так и с разложенным представлением буквы «ü», а также с символическими ссылками. Вы можете скачать файл образа диска здесь.
Структура каталога выглядит следующим образом:
$ 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
(То есть путь включает символическую ссылку на каталог и использует композицию юникода фактического файла, т. е. «ü» имени целевого файла предварительно составлено.)
| Метод | Результат |
|---|---|
| представление файловой системы | /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 | /Тома/Canonical_Normalize_Test/precomposed_u\U0308 |
Мы видим, что каждый метод дает разный результат:
Все они, по-видимому, нормализуют путь в NFD, т. Е. «ü» разлагается во всех случаях. Это необходимо и нормально для обычных томов без учета регистра, поскольку Погляди для имен файлов нечувствителен к нормализации. Однако: для томов, чувствительных к регистру, состав не должен изменяться, и, хотя я не проверял это, я предполагаю, что все вышеперечисленные функции обнаружат режим чувствительности тома к регистру и будут вести себя соответствующим образом.
Только NSURLCanonicalPathKey дает правильный результат, который необходим, если мы хотим повторно идентифицировать целевой элемент позже по пути (независимо от того, какая композиция Unicode используется и включает ли путь символические ссылки на каталог): он разрешает символическую ссылку каталога, но нет конечный символическая ссылка внутри symlink_target_dir. Если бы он разрешил последний элемент пути (как это делает URLByResolvingSymlinksInPath), вы не смогли бы ориентироваться на файлы символических ссылок.
fileSystemRepresentation NSString не изменяет путь (но нормализует его), тогда как URLByStandardizingPath NSURL изменяет путь в некоторых случаях (например, удаляя «/ private» из определенных корневых папок).
Только NSURLCanonicalPathKey исправит верхний/нижний регистр в зависимости от фактического пути на диске. Например, URL-адрес, созданный из «/applications», не будет преобразован в фактический путь «/Applications» ни одной из других функций.
Если вам нужно повторно идентифицировать путь позже, независимо от того, какое представление (нормализация, символические ссылки на каталоги) используется, используйте либо NSURLCanonicalPathKey, если вам нужно сохранить фактический элемент, даже если это символическая ссылка, либо используйте URLByResolvingSymlinksInPath, чтобы всегда идентифицировать цель любых символических ссылок, предоставленных вам.
Обратите внимание, однако (см. первый пример), что если вы используете URLByResolvingSymlinksInPath, «/private/var/tmp» и т. д. будут преобразованы в «/var/tmp» и т. д., что необычно, потому что тогда он все еще содержит символическую ссылку (т.е. « /вар").
Также имейте в виду, что регистр может быть неправильным, если вы не получите канонический путь. И чтобы компенсировать это, сравнение путей требует, чтобы вы сначала проверили, находится ли путь на томе без учета регистра или нет, чтобы вы использовали правильные параметры сравнения (и, в качестве дополнительной сложности, просто сравнивая пути с «нечувствительным к регистру» параметр может быть неправильным для некоторых редких сценариев на томах HFS+, поскольку они используют более старый стандарт Unicode, в котором были некоторые другие правила, чем те, которые используются в текущих версиях macOS).
Наконец, если вы просто хотите увидеть, указывают ли два пути на один и тот же файл, безопаснее использовать другие средства, не зависящие от путей. См. этот ответ. И если вам нужно постоянно запоминать расположение файлов, лучше всего использовать закладки, чтобы они были найдены даже в том случае, если пользователь за это время переименовал или переместил файл.
Отказ от ответственности: все эти результаты были получены эмпирическим путем при тестировании как на macOS 10.13.6, так и на 11.1 (и промежуточных системах), поэтому вы можете перепроверить мои выводы и оставить комментарий, если вы получите другие результаты.
Извиняюсь, что не увидел этого в ближайшее время. Отличный ответ, спасибо за вклад.