文件从NSLibraryDirectory中消失

bra*_*ipt 3 objective-c ios nsdocumentdirectory nslibrarydirectory

我将一些文件存储在iOS应用程序的Library目录中,使用以下方法构建它.最后,我可以调用[MyClass dataDirectory]我的文件处理,一切都很顺利.然而,我最近发现,一些文件似乎神秘地消失在这个目录之外.根据文件,情况并非如此.这是存储持久文件的安全位置吗?

该目录的控制台输出是: ~/var/mobile/Containers/Data/Application/{id}/Library/Data

+ (NSString*)libraryDirectory
{
    return [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
}

+ (NSString*)dataDirectory
{
    NSString* dir = [[self libraryDirectory] stringByAppendingPathComponent:@"Data"];
    BOOL isDir=NO;
    NSError * error = nil;
    NSFileManager *fileManager = [NSFileManager new];

    if (![fileManager fileExistsAtPath:dir isDirectory:&isDir] && isDir)
    {

        [[NSFileManager defaultManager] createDirectoryAtPath:dir
                                  withIntermediateDirectories:YES
                                                   attributes:nil
                                                        error:&error];
    }

    [self addSkipBackupAttributeToItemAtURL:[NSURL fileURLWithPath:dir isDirectory:YES]];

    if (error != nil) {
        DDLogError(@"Fatal error creating ~/Library/Data directory: %@", error);
    }
    return dir;
}
Run Code Online (Sandbox Code Playgroud)

和跳过方法:

+ (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
    if ([[NSFileManager defaultManager] fileExistsAtPath:[URL path]])
    {
        assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]);

        NSError *error = nil;
        BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]
                                      forKey: NSURLIsExcludedFromBackupKey error: &error];
        if(!success){
            DDLogError(@"Error excluding %@ from backup %@", [URL lastPathComponent], error);
        }
        return success;
    }
    return YES;
}
Run Code Online (Sandbox Code Playgroud)

que*_*ish 23

在您发布的代码中,第一个问题出现在:

if (![fileManager fileExistsAtPath:dir isDirectory:&isDir] && isDir)

在评估它时,isDir将默认为NO,如果文件不存在或不是目录,则将设置为NO.这将阻止创建目录.删除&& isDir或更改为|| !isDir获得所需的逻辑.

现在回答你原来的问题:

这是(NSLibraryDirectory的子目录)存储持久文件的安全位置吗?

是.NSLibraryDirectory默认情况下备份.要遵守iOS数据存储指南,应用程序不应将用户创建的数据存储在该位置,但它是存储应用程序数据的安全位置.NSApplicationSupportDirectory是一个目录,通常位于NSLibraryDirectory,并且是存储此类数据的首选位置.将备份该位置中的数据,并将在应用程序和操作系统更新期间迁移.

iOS的数据存储准则,文件系统编程指南,以及应用程序编程指南适用于iOS都提供在何处放置文件的指导,以及它们将如何从标准文件系统位置备份.

除非这些文件的NSURLIsExcludedFromBackupKey/ kCFURLIsExcludedFromBackupKey资源元数据值已更改.然后它变得更加复杂.

文件'从备份中排除'

通常,如果可以备份Documents目录之外的文件,系统会假定它也可以在低空间或其他条件下清除它.这就是为什么NSURLIsExcludedFromBackupKey在文件上设置为YES允许文件即使在低存储条件下也能保持原样的原因.如果您的应用程序将NSURLIsExcludedFromBackupKey文件设置为YES,则您的应用程序将承担该文件的生命周期.

这里的问题是备份过程和清除过程不遵循相同的逻辑.Apple的文档表明,为了控制备份行为,可以NSURLIsExcludedFromBackupKey在目录上进行设置.该目录的子级将有效地继承该资源值(实际上,这可能不准确).但是,清除过程似乎没有相同的行为.它可能无法检查父目录的备份排除并将其应用于子目录,因此如果文件没有NSURLIsExcludedFromBackupKey明确设置,则可能会清除它.

这变得更加复杂.如果您要阅读常量文档,NSURLIsExcludedFromBackupKey您会看到:

通常对用户文档进行的一些操作会导致此属性重置为false; 因此,请勿在用户文档上使用此属性.

这实际上不仅适用于用户文档.例如,如果要对文件执行原子写操作,例如:

[thing writeToURL:URL atomically:YES encoding:NSUTF8StringEncoding error:&error]

如果该文件在URLNSURLIsExcludedFromBackupKey写入前设置为YES,它现在似乎被设置为NO.像这样的原子写入将首先创建一个临时文件,写入该文件,并用新文件替换原始文件.这样做不会保留文件和URL资源标志.原始文件NSURLIsExcludedFromBackupKey设置了资源值,现在在同一位置新创建的文件没有.这只是一个例子; 许多Foundation API执行这样的原子写入.

有些情况会变得更加复杂.更新应用程序时,会将其安装到具有新应用程序容器路径的新位置.迁移旧应用程序容器内的数据.关于在更新过程中可能迁移或不迁移的内容的保证很少.它可能是一切,它可能只是一些东西.特别是没有关于如何NSURLIsExcludedFromBackupKey处理标有资源属性的文件或目录的指导.在实践中,似乎这些文件通常是最不可能迁移的文件,并且在迁移它们时,NSURLIsExcludedFromBackupKey很少保留该属性.

操作系统更新也是一个问题.历史上,Over-the-Air更新存在问题,并导致NSURLIsExcludedFromBackupKey资源属性被有效清除或忽略."主要"操作系统更新将清除设备并从备份恢复 - 这相当于迁移到新硬件.标记为NSURLIsExcludedFromBackupKeyresource属性的文件将不会迁移,应用程序将不得不重新创建它们.

TechNote 2285:测试iOS应用更新中描述了更新方案

因此,在使用NSURLIsExcludedFromBackupKey它时通常最好在每次访问时设置值,并且应始终通过文件协调API完成(除非您要写入共享组容器,这是一组完全不同的问题).如果NSURLIsExcludedFromBackupKey资源属性值丢失,则可以随时清除文件.理想情况下,应用程序不应该依赖于NSURLIsExcludedFromBackupKey操作系统可能(或可能不)处理它的方式,而应该设计为可以按需重建数据.这可能并不总是可能的.

从您的问题和您发布的代码中可以清楚地看出,您在某种程度上依赖于NSURLIsExcludedFromBackupKey确保您的文件具有应用程序控制的生命周期.正如您从上面所看到的,情况可能并非总是如此:有许多常见的情况,资源属性值可以消失,并且随之而来的是您的文件.

值得注意的是,NSFileProtection属性以相同的方式工作,并且可以在相同的场景中消失(还有一些).

TL; DR; 我该怎么办?

根据您的问题,代码和您所看到的行为的描述:

  • NSURLIsExcludedFromBackupKey在包含您要保留的文件的目录上设置值可能不足以阻止它们被清除.设置NSURLIsExcludedFromBackupKey对实际文件的每次访问都是明智的,而不仅仅是父目录.还要尝试确保在对文件进行任何写入后设置此资源值,尤其是通过可能正在执行原子写入的高级API等.

  • 所有NSFileManager和文件读/写操作都应该使用文件协调.即使在单线程的应用程序中,也会有其他进程与"您的"文件交互.进程类似于在低空间条件下运行备份或清除文件的守护程序.在您-fileExistsAtPath:-setResourceValue:forKey:error:另一个进程之间可以更改,删除或移动您的文件及其属性.-setResourceValue:forKey:error:在没有做任何事情的情况下,实际上会返回YES并且没有错误,例如文件不存在.

  • 标记的文件和目录NSURLIsExcludedFromBackupKey是应用程序要管理的责任.应用程序仍应在适当的时间清除这些文件或其内容,或对其增长设置限制.如果查看设备上的每个应用程序磁盘使用情况信息,您可能会猜到某些应用程序的名称无法正确执行此操作.

  • 测试更新方案,如技术说明2285:测试iOS应用更新中所述.经常.理想情况下,iOS模拟器将具有类似于模拟内存警告的"模拟低磁盘空间"功能,但此时却没有.

  • 如果可能的话,更改应用程序逻辑以重新创建这些文件(如果它们丢失).

  • @matt现在**是一个答案 (2认同)