Eh bien, je me suis peu aujourd'hui par quelque chose que j'aurais dû connaître mieux que de le faire. Ce fut une erreur classique de débutant ainsi, mais il m'a causé assez de chagrin que je ressens ça vaut moi embarrassante avec un poste de garde. Voici ce qui s'est passé:
Lorsque vous utilisez des données de base, Apple recommande à l'exception des morceaux relativement faible de données binaires, vous stockez des données binaires comme des images, des sons et des films dans le système de fichiers et non à l'intérieur de stockage persistant de données de base. Maintenant, dans mon expérience, SQLite peut manipuler les blobs, même très grande, sans charge supplémentaire trop, mais quand Apple fait une recommandation spécifique comme ça, je trouve que c'est généralement une bonne idée de suivre cette recommandation, sauf que j'ai une très bonne raison de ne pas. Dans ce cas, je n'ai pas.
Dans une application, je travaille sur un client, je suis d'enregistrement audio à l'aide AVAudioRecorder. Je ne demande pas à l'utilisateur de choisir un nom pour le fichier, je viens de stocker les enregistrements sous forme de fichiers CAF dans le système de fichiers en utilisant un nom de fichier basé sur la date et l'heure, comme ceci:
Puis, plus tard, après l'enregistrement est terminé, je stocke audioPathString l'intérieur un objet managé qui représente les enregistrements audio:
Puis, quand je veux jouer le son, je viens de tirer sur le chemin à partir des données de base et charger les données du système de fichiers, un peu comme ceci:
Et il a très bien fonctionné. Pendant un certain temps. Je ne pouvais quitter et commencer à sauvegarder et ce serait continuer à travailler. Et puis, à un certain moment, il ne serait pas plus de travail. Il ferait rapport OSStatus des codes de -43 ou -50 quand j'ai essayé de créer une instance de AVAudioPlayer, même si le fichier audio était encore dans le répertoire / documents. Je suis sûr que quelques-uns d'entre vous sont gloussant à vous-mêmes au sujet de mon erreur stupide, mais pour ceux qui ne le voyez pas, je vais vous expliquer.
Le chemin complet d'un élément dans le répertoire Documents de sandbox d'une application comprend pas le nom de l'application, mais un UUID (identifiant unique) à la place. Lorsque vous générez et exécutez votre application, un nouvel UUID est calculé si, et seulement si, quelque chose a changé depuis votre dernière génération. Si vous n'avez pas apporté de changements, il reste actif avec l'UUID précédente. Une fois que vous apporter quelques modifications au code ou à une ressource et re-compiler, les changements UUID, et le chemin stocké absolue ne pointe plus vers le bon endroit. La portion de l'URL qui représente l'UUID de l'application a maintenant changé. Xcode est assez intelligent pour se déplacer / de l'application Documents répertoire vers le nouvel emplacement afin de ne pas perdre toutes vos données tout en développant, mais toutes ces données est maintenant à un nouvel emplacement dans le système de fichiers.
Alors, comment gérez-vous cette situation? Le plus simple est de simplement stocker le nom de fichier et puis re-construire le chemin absolu à l'exécution en supposant que le fichier est dans le répertoire de votre application / Documents. Ainsi, au lieu de stocker le chemin d'recorderFilePath comme je le faisais dans l'échantillon de code précédent, vous auriez juste le magasin de dernier composant du chemin en utilisant la méthode de NSString lastPathComponent, comme ceci:
Puis, quand il est temps de charger le fichier, vous avez juste re-construire le chemin complet vers le fichier basé sur l'UUID actuel de l'application, comme ceci:
En utilisant cette approche, votre code va continuer à fonctionner même entre les versions de développement ou des rejets de demande, et vous ne perdrez pas une heure à essayer de comprendre pourquoi vos fichiers parfaitement valide ne se charge pas.
Lorsque vous utilisez des données de base, Apple recommande à l'exception des morceaux relativement faible de données binaires, vous stockez des données binaires comme des images, des sons et des films dans le système de fichiers et non à l'intérieur de stockage persistant de données de base. Maintenant, dans mon expérience, SQLite peut manipuler les blobs, même très grande, sans charge supplémentaire trop, mais quand Apple fait une recommandation spécifique comme ça, je trouve que c'est généralement une bonne idée de suivre cette recommandation, sauf que j'ai une très bonne raison de ne pas. Dans ce cas, je n'ai pas.
Dans une application, je travaille sur un client, je suis d'enregistrement audio à l'aide AVAudioRecorder. Je ne demande pas à l'utilisateur de choisir un nom pour le fichier, je viens de stocker les enregistrements sous forme de fichiers CAF dans le système de fichiers en utilisant un nom de fichier basé sur la date et l'heure, comme ceci:
NSString *audioPathString = [NSString stringWithFormat:@"%@/%@", DOCUMENTS_FOLDER, [item valueForKey:@"audioPath"]];Puis, plus tard, après l'enregistrement est terminé, je stocke audioPathString l'intérieur un objet managé qui représente les enregistrements audio:
audioItem.audioPath = audioPathString;Puis, quand je veux jouer le son, je viens de tirer sur le chemin à partir des données de base et charger les données du système de fichiers, un peu comme ceci:
NSError *error;
NSURL *url = [NSURL fileURLWithPath:item.audioPath];
AVAudioPlayer *player =[[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];Et il a très bien fonctionné. Pendant un certain temps. Je ne pouvais quitter et commencer à sauvegarder et ce serait continuer à travailler. Et puis, à un certain moment, il ne serait pas plus de travail. Il ferait rapport OSStatus des codes de -43 ou -50 quand j'ai essayé de créer une instance de AVAudioPlayer, même si le fichier audio était encore dans le répertoire / documents. Je suis sûr que quelques-uns d'entre vous sont gloussant à vous-mêmes au sujet de mon erreur stupide, mais pour ceux qui ne le voyez pas, je vais vous expliquer.
Tip: Si vous vous trouvez dans une situation où une NSError vous donne rien de plus qu'un code d'erreur ou d'OSStatus OSErr, vous pouvez découvrir ce que le code d'erreur signifie en laissant tomber vers le terminal et en tapant macerror suivi par le code d'erreur qui vous intéresse, y compris le - (ces codes d'erreur de style ancien sont les nombres négatifs).
Le chemin complet d'un élément dans le répertoire Documents de sandbox d'une application comprend pas le nom de l'application, mais un UUID (identifiant unique) à la place. Lorsque vous générez et exécutez votre application, un nouvel UUID est calculé si, et seulement si, quelque chose a changé depuis votre dernière génération. Si vous n'avez pas apporté de changements, il reste actif avec l'UUID précédente. Une fois que vous apporter quelques modifications au code ou à une ressource et re-compiler, les changements UUID, et le chemin stocké absolue ne pointe plus vers le bon endroit. La portion de l'URL qui représente l'UUID de l'application a maintenant changé. Xcode est assez intelligent pour se déplacer / de l'application Documents répertoire vers le nouvel emplacement afin de ne pas perdre toutes vos données tout en développant, mais toutes ces données est maintenant à un nouvel emplacement dans le système de fichiers.
Alors, comment gérez-vous cette situation? Le plus simple est de simplement stocker le nom de fichier et puis re-construire le chemin absolu à l'exécution en supposant que le fichier est dans le répertoire de votre application / Documents. Ainsi, au lieu de stocker le chemin d'recorderFilePath comme je le faisais dans l'échantillon de code précédent, vous auriez juste le magasin de dernier composant du chemin en utilisant la méthode de NSString lastPathComponent, comme ceci:
newItem.audioPath = [recorderFilePath lastPathComponent];
Puis, quand il est temps de charger le fichier, vous avez juste re-construire le chemin complet vers le fichier basé sur l'UUID actuel de l'application, comme ceci:
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *audioPathString = [NSString stringWithFormat:@"%@/%@", documentsDirectory, [item valueForKey:@"audioPath"]];En utilisant cette approche, votre code va continuer à fonctionner même entre les versions de développement ou des rejets de demande, et vous ne perdrez pas une heure à essayer de comprendre pourquoi vos fichiers parfaitement valide ne se charge pas.
Aucun commentaire:
Enregistrer un commentaire