Что указывает префикс /private на пути к файлу iOS?

У меня есть ошибка, когда мое приложение работает на iPhone, но не тогда, когда оно запускается на симуляторе. Я использовал длину пути к домашнему каталогу для извлечения относительного пути файла в /Documents. К сожалению, это не всегда корректно работает на iPhone, потому что префикс "/private" добавляется к домашнему пути. Однако с префиксом или без него на тот же файл ссылаются ok. Следующий код демонстрирует эту несогласованность. Какова цель "/private" и когда она предоставляется iOS?

- (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 записывается в журнал. Я вручную отделил вывод, чтобы показать разницу между строками 1 и 2.

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

Ответ 1

Я попробовал это из отладчика и обнаружил, что 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. Однако, Кевин-Баллард указывает, что это неверно. Я подтвердил, что он прав, и /var является символической ссылкой на /private/var (sigh)

(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

Чтобы ответить на ваш вопрос:

Я считаю, что /private был добавлен префикс, когда они выпустили OS X (я не думаю, что он был там в NeXTStep, но это были десятилетия). Кажется, существует дом etc, var и tmp (и, что странно, tftpboot; я не знал, что мой PBG4 может это сделать), возможно, пользователи не задаются вопросом, что это за глупая папка называется etc и пытается удалить его.

На устройстве Apple решила сохранить пользовательские данные в /private/var/mobile (имя пользователя "mobile" ). Я не уверен, почему они не выбрали /Users/mobile или просто /mobile, но это не имеет никакого значения, чем /var/mobile будет для "нормальной" Unix.

В симуляторе ваша учетная запись пользователя не может писать на /var (по уважительной причине). Данные пользователя хранятся где-то в ~/Library/Application Support/iPhone Simulator. В какой-то момент они начали использовать разные каталоги для разных версий симулятора.

Ответ 3

В Swift 3, URL имеет свойство standardizedFileUrl, которое удалит любые символические ссылки и разрешит относительные части в пути, например ./.

Как пишет документация, довольно бесполезно, но похоже, что она эквивалентна NSURL standardized.

Ответ 4

/var является просто символической ссылкой на /private/var. Таким образом, первый путь - это логический путь, к которому вы пытались получить доступ. Второй - это тот же путь с расширением символических ссылок.