Les files disparaissent de NSLibraryDirectory

Je stocke certains files dans le directory de la bibliothèque dans une application iOS, en utilisant les methods suivantes pour le build. En fin de count, je peux appeler [MyClass dataDirectory] pour gérer mes files et tout va bien. J'ai récemment découvert, cependant, que certains files semblent disparaître mystérieusement de ce directory. Selon la documentation , cela ne devrait pas être le cas. Est-ce un endroit sûr pour stocker des files persistants?

La sortie de console de ce directory est: ~/var/mobile/Containers/Data/Application/{id}/Library/Data

 + (NSSsortingng*)libraryDirectory { return [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject]; } + (NSSsortingng*)dataDirectory { NSSsortingng* dir = [[self libraryDirectory] ssortingngByAppendingPathComponent:@"Data"]; BOOL isDir=NO; NSError * error = nil; NSFileManager *fileManager = [NSFileManager new]; if (![fileManager fileExistsAtPath:dir isDirectory:&isDir] && isDir) { [[NSFileManager defaultManager] createDirectoryAtPath:dir withIntermediateDirectories:YES atsortingbutes:nil error:&error]; } [self addSkipBackupAtsortingbuteToItemAtURL:[NSURL fileURLWithPath:dir isDirectory:YES]]; if (error != nil) { DDLogError(@"Fatal error creating ~/Library/Data directory: %@", error); } return dir; } 

Et la méthode de saut:

 + (BOOL)addSkipBackupAtsortingbuteToItemAtURL:(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; } 

Dans le code que vous avez posté, le premier problème est ici:

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

Au point où cela est évalué, isDir aura la valeur par défaut NO, et sera défini sur NO si le file n'existe pas ou n'est pas un directory. Cela empêchera la création du directory. Supprimer && isDir ou modifier en || !isDir || !isDir pour get la logique que vous voulez.

Maintenant à votre question originale:

Est-ce (un sous-directory de NSLibraryDirectory) un endroit sûr pour stocker des files persistants?

Oui. NSLibraryDirectory est sauvegardé par défaut. Pour respecter les consignes de stockage de données iOS, une application ne doit pas stocker datatables créées par l'user à cet location, mais il s'agit d'un location sûr pour stocker datatables de l'application. NSApplicationSupportDirectory est un directory qui se trouve généralement dans NSLibraryDirectory et est l'location préféré pour stocker ce type de données. Les données de cet location seront sauvegardées et migrées lors des mises à jour de l'application et du operating system.

Les consignes de stockage des données iOS , le Guide de programmation des filesystems et le Guide de programmation des applications pour iOS fournissent tous des indications sur l'location des files et la manière dont ils seront sauvegardés à partir des locations de système de files standard.

À less que ces files aient eu leur valeur de métadonnées de ressource NSURLIsExcludedFromBackupKey / kCFURLIsExcludedFromBackupKey modifiée. Ensuite, cela devient beaucoup plus compliqué.

Fichiers 'exclus de la sauvegarde'

Généralement, si un file en dehors d'un directory Documents peut être sauvegardé, le système suppose qu'il peut également le purger dans un espace réduit ou dans d'autres conditions. C'est pourquoi la définition de NSURLIsExcludedFromBackupKey sur YES sur un file permet au file de persister même dans des conditions de stockage faibles. Si votre application définit NSURLIsExcludedFromBackupKey sur YES pour un file, votre application assume la responsabilité de la durée de vie de ce file.

Le problème ici est que le process de sauvegarde et le process de purge ne suivent pas la même logique. La documentation d'Apple indique que pour contrôler le comportement de sauvegarde, il est possible de définir NSURLIsExcludedFromBackupKey sur un directory. Les enfants de ce directory hériteront effectivement de cette valeur de ressource (dans la pratique, cela peut ne pas être exact). Le process de purge, cependant, ne semble pas avoir le même comportement. Il ne peut pas vérifier les exclusions de sauvegarde des directorys parents et les appliquer aux enfants, et par conséquent, si un file n'a pas explicitement défini NSURLIsExcludedFromBackupKey il peut être purgé.

Cela devient encore plus compliqué. Si vous deviez lire la documentation de la constante NSURLIsExcludedFromBackupKey vous NSURLIsExcludedFromBackupKey :

Certaines opérations généralement effectuées sur les documents user provoquent la réinitialisation de cette propriété à false; par conséquent, n'utilisez pas cette propriété sur les documents user.

Cela s'applique en réalité à bien plus qu'à des documents user. Par exemple, si vous deviez effectuer une écriture atomique sur un file tel que:

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

Si le file à l' URL avait NSURLIsExcludedFromBackupKey défini sur YES avant l'écriture, il semblerait maintenant être défini sur NO. Une écriture atomique comme celle-ci crée d'abord un file temporaire, écrit dessus et remplace l'original par le nouveau file. Ce faisant, les indicateurs de ressource de file et d'URL ne sont pas conservés. Le file d'origine possédait la valeur de ressource NSURLIsExcludedFromBackupKey , ce qui n'est pas le cas du file nouvellement créé au même location. Ceci est juste un exemple; de nombreuses API Foundation exécutent des écritures atomiques comme ceci implicitement.

Il y a des scénarios où cela devient encore plus complexe. Lorsqu'une application est mise à jour, elle est installée dans un nouvel location avec un nouveau path de conteneur d'application. Les données à l'intérieur de l'ancien conteneur d'application sont migrées. Il existe peu de garanties concernant ce qui peut ou non être migré dans le cadre du process de mise à jour. Cela peut être tout, peut-être seulement certaines choses. En particulier, il n'y a aucune indication sur la façon dont les files ou les directorys marqués avec l' NSURLIsExcludedFromBackupKey ressource NSURLIsExcludedFromBackupKey seront traités. En pratique, il semble que ce sont souvent les files les less susceptibles d'être migrés, et lorsqu'ils sont migrés, l'atsortingbut NSURLIsExcludedFromBackupKey est rarement conservé.

Les mises à jour du operating system sont également un problème. Historiquement, les mises à jour Over-The-Air ont été problématiques et ont provoqué l' NSURLIsExcludedFromBackupKey l' NSURLIsExcludedFromBackupKey l'atsortingbut de ressource NSURLIsExcludedFromBackupKey . Une mise à jour «majeure» de l'OS effacera l'appareil et restaurera à partir d'une sauvegarde – ce qui équivaut à une migration vers un nouveau matériel. Les files marqués avec l' NSURLIsExcludedFromBackupKey ressource NSURLIsExcludedFromBackupKey ne seront pas migrés et l'application devra les recréer.

Les scénarios de mise à jour sont décrits dans TechNote 2285: Test des mises à jour de l'application iOS

Pour cette raison, lorsque vous utilisez NSURLIsExcludedFromBackupKey il est généralement préférable de définir la valeur sur chaque access et, comme toujours, via les API de coordination de files (sauf si vous écrivez dans un conteneur de groupe partagé, qui est un set de problèmes entièrement différent) . Si la valeur de l'atsortingbut de ressource NSURLIsExcludedFromBackupKey est perdue, les files peuvent être purgés à tout moment. Idéalement, une application ne devrait pas dépendre de la NSURLIsExcludedFromBackupKey ou de la façon dont le operating system peut (ou non!) La gérer, mais plutôt être conçue de telle sorte que datatables puissent être recréées à la request. Ce n'est pas toujours possible.

Il ressort clairement de votre question et du code que vous avez publié que vous NSURLIsExcludedFromBackupKey un peu de NSURLIsExcludedFromBackupKey pour NSURLIsExcludedFromBackupKey assurer que vos files ont une durée de vie contrôlée par l'application. Comme vous pouvez le voir ci-dessus, cela peut ne pas toujours être le cas: il existe de nombreux scénarios communs dans lesquels cette valeur d'atsortingbut de ressource peut disparaître, et avec elle vos files.

Il convient également de noter que les attributes NSFileProtection fonctionnent de la même manière et peuvent disparaître dans les mêmes scénarios (et quelques autres).

TL; DR; Que devrais-je faire?

En fonction de votre question, du code et de la description du comportement que vous voyez:

  • Définir la valeur NSURLIsExcludedFromBackupKey sur le directory contenant le (s) file (s) que vous souhaitez conserver peut ne pas être suffisant pour les empêcher d'être purgé. Il serait sage de définir NSURLIsExcludedFromBackupKey sur chaque access aux files réels, plutôt que juste un directory parent. Essayez également de vous assurer que cette valeur de ressource est définie après toute écriture dans le file, en particulier via une API de haut niveau pouvant effectuer des écritures atomiques, etc.

  • Toutes les opérations NSFileManager et de lecture / écriture de files doivent utiliser la coordination de files. Même dans une application à un seul thread, d'autres process interagiront avec "vos" files. Les process tels que les démons exécutant des sauvegardes ou purger des files dans des conditions d'espace restreint. Entre votre -fileExistsAtPath: et l' -setResourceValue:forKey:error: autre process pourrait modifier, supprimer ou déplacer votre file et ses attributes. -setResourceValue:forKey:error: returnnera YES et pas d'erreur dans de nombreux cas où il n'a rien fait, comme le file n'existe pas.

  • Les files et directorys marqués avec NSURLIsExcludedFromBackupKey sont la responsabilité de l'application à gérer. L'application doit toujours purger ces files ou leur contenu à un moment approprié, ou fixer des limites à leur croissance. Si vous consultez les informations d'utilisation de disque par application sur un périphérique, vous pouvez probablement deviner le nom de certaines applications qui ne le font pas correctement.

  • Tester les scénarios de mise à jour comme décrit dans TechNote 2285: Test des mises à jour de l'application iOS . souvent. Idéalement, le simulateur iOS aurait une capacité «Simuler un espace disque faible» similaire à la simulation d'avertissements de memory, mais pour le moment, ce n'est pas le cas.

  • Dans la mesure du possible, modifiez la logique de l'application pour recréer ces files s'ils disparaissent.

Dans la documentation que vous avez liée, il est indiqué que
Les données critiques doivent être stockées dans le directory / Documents. Les données critiques sont toutes datatables qui ne peuvent pas être recréées par votre application, telles que les documents user et autres contenus générés par les users.

Il est également mentionné que
Les données mises en cache doivent être stockées dans le directory / Library / Caches. Les exemples de files que vous devez placer dans le directory Caches incluent (mais ne sont pas limités à) des files de cache de database et du contenu téléchargeable, tels que ceux utilisés dans les applications de magazines, de journaux et de maps. Votre application doit être capable de gérer avec élégance les situations dans lesquelles datatables mises en cache sont supprimées par le système pour libérer de l'espace disque.

Le directory que vous utilisez n'est pas explicitement mentionné pour le stockage des données user, il est utilisé par le système et n'est pas sauvegardé pour vos données. Il est garanti d'être épargné par une mise à jour de votre application, mais c'est tout

Pour find le dossier des documents, vous pouvez faire quelque chose comme

 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSSsortingng *documentsFolderPath = [paths firstObject];