tutorial que persistencia datos data objective-c ios core-data

objective-c - persistencia - que es core data



¿Puedo acceder a los archivos utilizados para el almacenamiento binario externo en Core Data? (2)

Estoy trabajando en una aplicación de base de datos multimedia. Tengo un modelo personalizado con almacenamiento de datos y pienso en reescribirlo en Core Data. Un caso de uso que es de particular interés para mí es el almacenamiento de películas. Guardo archivos de películas en la base de datos, pero la estructura de medios solo puede leer películas de archivos (no datos).

Core Data ofrece una característica útil llamada "almacenamiento binario externo", donde los datos de la entidad no se almacenan en la base de datos, sino en un archivo externo. Esto es transparente para el usuario de Core Data API. Mi pregunta es, ¿puedo obtener la ruta al archivo externo, para poder almacenar una película usando Core Data y luego cargarla fácilmente desde su archivo externo Core Data?


Si desea acceder a los datos directamente (es decir, no a través de CoreData), es mejor que le dé a cada archivo un UUID como nombre, y almacene ese nombre en la base de datos, y almacene el archivo usted mismo.

Si usa UIManagedDocument, tiene varias opciones. Usando la técnica anterior, puede almacenar los archivos junto con la base de datos, porque UIManagedDocument es realmente un paquete de archivos.

Alternativamente, puede crear una subclase desde UIManagedDocument y anular los métodos que manejan la lectura / escritura de los archivos "adicionales". Esto le dará acceso a los archivos ellos mismos. Puede enganchar allí para hacer lo que quiera, incluso agarrar la URL real al archivo que CoreData crea automáticamente.

- (id)additionalContentForURL:(NSURL *)absoluteURL error:(NSError **)error - (BOOL)readAdditionalContentFromURL:(NSURL *)absoluteURL error:(NSError **)error - (BOOL)writeAdditionalContent:(id)content toURL:(NSURL *)absoluteURL originalContentsURL:(NSURL *)absoluteOriginalContentsURL error:(NSError **)error


Sí, PUEDE acceder a los archivos almacenados en Almacenamiento externo. Se necesita un poco de pirateo, y puede no ser completamente kosher con la App Store de Apple, pero puedes hacerlo con bastante facilidad.

Suponiendo que tenemos una Subclase NSManagedObject ''Media'', con una propiedad ''data'' que se ha establecido en ''Allows External Storage'' en el Core Data Editor:

// Media.h // Examples // // Created by Garrett Shearer on 11/21/12. // Copyright (c) 2012 Garrett Shearer. All rights reserved. // #import <Foundation/Foundation.h> #import <CoreData/CoreData.h> @interface CRMMedia : NSManagedObject @property (nonatomic, retain) NSString * name; @property (nonatomic, retain) NSData * data; @end

Y una categoría NSString handy-dandy:

// NSString+Parse.m // Examples // // Created by Garrett Shearer on 11/21/12. // Copyright (c) 2012 Garrett Shearer. All rights reserved. // #import "NSString+Parse.h" @implementation NSString (Parse) - (NSString*)returnBetweenString:(NSString *)inString1 andString:(NSString *)inString2 { NSRange substringRange = [self rangeBetweenString:inString1 andString:inString2]; logger(@"substringRange: (%d, %d)",substringRange.location,substringRange.length); logger(@"string (self): %@",self); return [self substringWithRange:substringRange]; } /* Return the range of a substring, searching between a starting and ending delimeters Original Source: <http://cocoa.karelia.com/Foundation_Categories/NSString/Return_the_range_of.m> (See copyright notice at <http://cocoa.karelia.com>) */ /*" Find a string between the two given strings with the default options; the delimeter strings are not included in the result. "*/ - (NSRange) rangeBetweenString:(NSString *)inString1 andString:(NSString *)inString2 { return [self rangeBetweenString:inString1 andString:inString2 options:0]; } /*" Find a string between the two given strings with the given options inMask; the delimeter strings are not included in the result. The inMask parameter is the same as is passed to [NSString rangeOfString:options:range:]. "*/ - (NSRange) rangeBetweenString:(NSString *)inString1 andString:(NSString *)inString2 options:(unsigned)inMask { return [self rangeBetweenString:inString1 andString:inString2 options:inMask range:NSMakeRange(0,[self length])]; } /*" Find a string between the two given strings with the given options inMask and the given substring range inSearchRange; the delimeter strings are not included in the result. The inMask parameter is the same as is passed to [NSString rangeOfString:options:range:]. "*/ - (NSRange) rangeBetweenString:(NSString *)inString1 andString:(NSString *)inString2 options:(unsigned)inMask range:(NSRange)inSearchRange { NSRange result; unsigned int foundLocation = inSearchRange.location; // if no start string, start here NSRange stringEnd = NSMakeRange(NSMaxRange(inSearchRange),0); // if no end string, end here NSRange endSearchRange; if (nil != inString1) { // Find the range of the list start NSRange stringStart = [self rangeOfString:inString1 options:inMask range:inSearchRange]; if (NSNotFound == stringStart.location) { return stringStart; // not found } foundLocation = NSMaxRange(stringStart); } endSearchRange = NSMakeRange( foundLocation, NSMaxRange(inSearchRange) - foundLocation ); if (nil != inString2) { stringEnd = [self rangeOfString:inString2 options:inMask range:endSearchRange]; if (NSNotFound == stringEnd.location) { return stringEnd; // not found } } result = NSMakeRange( foundLocation, stringEnd.location - foundLocation ); return result; } @end

Ahora es el momento de algo de magia ... Vamos a crear un método de Categoría que analiza el nombre de archivo de la cadena [data description]. Cuando se opera en una instancia de la subclase de Medios, ''datos'' es en realidad una ''Referencia de Almacenamiento Externo'', no un objeto NSData. El nombre de archivo de los datos reales se almacena en la cadena de descripción.

// Media+ExternalData.m // Examples // // Created by Garrett Shearer on 11/21/12. // Copyright (c) 2012 Garrett Shearer. All rights reserved. // #import "Media+ExternalData.h" #import "NSString+Parse.h" @implementation Media (ExternalData) - (NSString*)filePathString { // Parse out the filename NSString *description = [self.data description]; NSString *filename = [description returnBetweenString:@"path = " andString:@" ;"]; // Determine the name of the store NSPersistentStoreCoordinator *psc = self.managedObjectContext.persistentStoreCoordinator; NSPersistentStore *ps = [psc.persistentStores objectAtIndex:0]; NSURL *storeURL = [psc URLForPersistentStore:ps]; NSString *storeNameWithExt = [storeURL lastPathComponent]; NSString *storeName = [storeNameWithExt stringByDeletingPathExtension]; // Generate path to the ''external data'' directory NSString *documentsPath = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] path]; NSString *pathComponentToExternalStorage = [NSString stringWithFormat:@".%@_SUPPORT/_EXTERNAL_DATA",storeName]; NSString *pathToExternalStorage = [documentsPath stringByAppendingPathComponent:pathComponentToExternalStorage]; // Generate path to the media file NSString *pathToMedia = [pathToExternalStorage stringByAppendingPathComponent:filename]; logger(@"pathToMedia: %@",pathToMedia); return pathToMedia; } - (NSURL*)filePathUrl { NSURL *urlToMedia = [NSURL fileURLWithPath:[self filePathString]]; return urlToMedia; } @end

Ahora tiene una ruta NSString y una ruta NSURL al archivo. ¡¡¡ALEGRÍA!!!

Algo para tener en cuenta, he tenido problemas al cargar películas con este método ... pero también se me ocurrió una solución alternativa. Parece que MPMoviePlayer no accederá a los archivos en este directorio, por lo que la solución fue copiar temporalmente el archivo en el directorio de documentos y reproducirlo. A continuación, elimine la copia temporal cuando descargue mi vista:

- (void)viewDidLoad { [super viewDidLoad]; [self copyTmpFile]; } - (void)viewDidUnload { logger(@"viewDidUnload"); [_moviePlayer stop]; [_moviePlayer.view removeFromSuperview]; [self cleanupTmpFile]; [super viewDidUnload]; } - (NSString*)tmpFilePath { NSString *documentsPath = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] path]; NSString *tmpFilePath = [documentsPath stringByAppendingPathComponent:@"temp_video.m4v"]; return tmpFilePath; } - (void)copyTmpFile { NSString *tmpFilePath = [self tmpFilePath]; NSFileManager *mgr = [NSFileManager defaultManager]; NSError *err = nil; if([mgr fileExistsAtPath:tmpFilePath]) { [mgr removeItemAtPath:tmpFilePath error:nil]; } [mgr copyItemAtPath:_media.filePathString toPath:tmpFilePath error:&err]; if(err) { logger(@"error: %@",err.description); } } - (void)cleanupTmpFile { NSString *tmpFilePath = [self tmpFilePath]; NSFileManager *mgr = [NSFileManager defaultManager]; if([mgr fileExistsAtPath:tmpFilePath]) { [mgr removeItemAtPath:tmpFilePath error:nil]; } }

¡Buena suerte!