tutorial cocoa-touch

cocoa-touch - cocoa touch tutorial



Almacenamiento de objetos personalizados en un NSMutableArray en NSUserDefaults (5)

Recientemente he intentado almacenar los resultados de búsqueda de mi aplicación de iPhone en la colección NSUserDefaults. También uso esto para guardar la información de registro del usuario con éxito, pero por alguna razón, intentar almacenar mi NSMutableArray de clases de ubicación personalizadas siempre vuelve vacío.

Intenté convertir NSMutableArray en un elemento NSData a partir de esta publicación, pero obtengo el mismo resultado (¿es posible guardar una matriz de enteros utilizando NSUserDefaults en iPhone? )

Los ejemplos de código que he probado son:

Salvar:

[prefs setObject:results forKey:@"lastResults"]; [prefs synchronize];

o

NSData *data = [NSData dataWithBytes:&results length:sizeof(results)]; [prefs setObject:data forKey:@"lastResults"];

o

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:results]; [prefs setObject:data forKey:@"lastResults"];

Carga:

lastResults = (NSMutableArray *)[prefs objectForKey:@"lastResults"];

o

NSData *data = [prefs objectForKey:@"lastResults"]; memcpy(&lastResults, data.bytes, data.length);

o

NSData *data = [prefs objectForKey:@"lastResults"]; lastResults = [NSKeyedUnarchiver unarchiveObjectWithData:data];

Después de seguir los consejos a continuación, también he implementado NSCoder en mi objeto (ignore el uso excesivo de NSString como temporal):

#import "Location.h" @implementation Location @synthesize locationId; @synthesize companyName; @synthesize addressLine1; @synthesize addressLine2; @synthesize city; @synthesize postcode; @synthesize telephoneNumber; @synthesize description; @synthesize rating; @synthesize priceGuide; @synthesize latitude; @synthesize longitude; @synthesize userLatitude; @synthesize userLongitude; @synthesize searchType; @synthesize searchId; @synthesize distance; @synthesize applicationProviderId; @synthesize contentProviderId; - (id) initWithCoder: (NSCoder *)coder { if (self = [super init]) { self.locationId = [coder decodeObjectForKey:@"locationId"]; self.companyName = [coder decodeObjectForKey:@"companyName"]; self.addressLine1 = [coder decodeObjectForKey:@"addressLine1"]; self.addressLine2 = [coder decodeObjectForKey:@"addressLine2"]; self.city = [coder decodeObjectForKey:@"city"]; self.postcode = [coder decodeObjectForKey:@"postcode"]; self.telephoneNumber = [coder decodeObjectForKey:@"telephoneNumber"]; self.description = [coder decodeObjectForKey:@"description"]; self.rating = [coder decodeObjectForKey:@"rating"]; self.priceGuide = [coder decodeObjectForKey:@"priceGuide"]; self.latitude = [coder decodeObjectForKey:@"latitude"]; self.longitude = [coder decodeObjectForKey:@"longitude"]; self.userLatitude = [coder decodeObjectForKey:@"userLatitude"]; self.userLongitude = [coder decodeObjectForKey:@"userLongitude"]; self.searchType = [coder decodeObjectForKey:@"searchType"]; self.searchId = [coder decodeObjectForKey:@"searchId"]; self.distance = [coder decodeObjectForKey:@"distance"]; self.applicationProviderId = [coder decodeObjectForKey:@"applicationProviderId"]; self.contentProviderId = [coder decodeObjectForKey:@"contentProviderId"]; } } - (void) encodeWithCoder: (NSCoder *)coder { [coder encodeObject:locationId forKey:@"locationId"]; [coder encodeObject:companyName forKey:@"companyName"]; [coder encodeObject:addressLine1 forKey:@"addressLine1"]; [coder encodeObject:addressLine2 forKey:@"addressLine2"]; [coder encodeObject:city forKey:@"city"]; [coder encodeObject:postcode forKey:@"postcode"]; [coder encodeObject:telephoneNumber forKey:@"telephoneNumber"]; [coder encodeObject:description forKey:@"description"]; [coder encodeObject:rating forKey:@"rating"]; [coder encodeObject:priceGuide forKey:@"priceGuide"]; [coder encodeObject:latitude forKey:@"latitude"]; [coder encodeObject:longitude forKey:@"longitude"]; [coder encodeObject:userLatitude forKey:@"userLatitude"]; [coder encodeObject:userLongitude forKey:@"userLongitude"]; [coder encodeObject:searchType forKey:@"searchType"]; [coder encodeObject:searchId forKey:@"searchId"]; [coder encodeObject:distance forKey:@"distance"]; [coder encodeObject:applicationProviderId forKey:@"applicationProviderId"]; [coder encodeObject:contentProviderId forKey:@"contentProviderId"]; }


Consulte "¿Por qué NSUserDefaults no pudo guardar NSMutableDictionary en iPhone SDK?" ( ¿Por qué NSUserDefaults no pudo guardar NSMutableDictionary en iPhone SDK? )

Si desea (de) serializar objetos personalizados, debe proporcionar las funciones para (de) serializar los datos (protocolo NSCoding). La solución a la que se refiere funciona con la matriz int porque la matriz no es un objeto sino una porción contigua de memoria.


Creo que ha recibido un error en su método initWithCoder, al menos en el código proporcionado no devuelve el objeto ''self''.

- (id) initWithCoder: (NSCoder *)coder { if (self = [super init]) { self.locationId = [coder decodeObjectForKey:@"locationId"]; self.companyName = [coder decodeObjectForKey:@"companyName"]; self.addressLine1 = [coder decodeObjectForKey:@"addressLine1"]; self.addressLine2 = [coder decodeObjectForKey:@"addressLine2"]; self.city = [coder decodeObjectForKey:@"city"]; self.postcode = [coder decodeObjectForKey:@"postcode"]; self.telephoneNumber = [coder decodeObjectForKey:@"telephoneNumber"]; self.description = [coder decodeObjectForKey:@"description"]; self.rating = [coder decodeObjectForKey:@"rating"]; self.priceGuide = [coder decodeObjectForKey:@"priceGuide"]; self.latitude = [coder decodeObjectForKey:@"latitude"]; self.longitude = [coder decodeObjectForKey:@"longitude"]; self.userLatitude = [coder decodeObjectForKey:@"userLatitude"]; self.userLongitude = [coder decodeObjectForKey:@"userLongitude"]; self.searchType = [coder decodeObjectForKey:@"searchType"]; self.searchId = [coder decodeObjectForKey:@"searchId"]; self.distance = [coder decodeObjectForKey:@"distance"]; self.applicationProviderId = [coder decodeObjectForKey:@"applicationProviderId"]; self.contentProviderId = [coder decodeObjectForKey:@"contentProviderId"]; } return self; // this is missing in the example above }

yo suelo

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:results]; [prefs setObject:data forKey:@"lastResults"];

y

NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults]; NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:@"lastResults"]; if (dataRepresentingSavedArray != nil) { NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedArray]; if (oldSavedArray != nil) objectArray = [[NSMutableArray alloc] initWithArray:oldSavedArray]; else objectArray = [[NSMutableArray alloc] init]; }

y funciona perfecto para mi

Con saludos,

Stefan


Creo que parece que el problema con tu código no es guardar la matriz de resultados. Está cargando los datos intenta usando

lastResults = [prefs arrayForKey:@"lastResults"];

Esto devolverá la matriz especificada por la clave.


Para cargar objetos personalizados en una matriz, esto es lo que he usado para tomar la matriz:

NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults]; NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:@"savedArray"]; if (dataRepresentingSavedArray != nil) { NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedArray]; if (oldSavedArray != nil) objectArray = [[NSMutableArray alloc] initWithArray:oldSavedArray]; else objectArray = [[NSMutableArray alloc] init]; }

Debería verificar que los datos devueltos por los valores predeterminados del usuario no sean nulos, porque creo que la desinstalación desde cero provoca un bloqueo.

Archivar es simple, utilizando el siguiente código:

[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:objectArray] forKey:@"savedArray"];

Como señaló f3lix, debe hacer que su objeto personalizado cumpla con el protocolo NSCoding. Agregar métodos como los siguientes debería hacer el truco:

- (void)encodeWithCoder:(NSCoder *)coder; { [coder encodeObject:label forKey:@"label"]; [coder encodeInteger:numberID forKey:@"numberID"]; } - (id)initWithCoder:(NSCoder *)coder; { self = [super init]; if (self != nil) { label = [[coder decodeObjectForKey:@"label"] retain]; numberID = [[coder decodeIntegerForKey:@"numberID"] retain]; } return self; }


Yo recomendaría no tratar de almacenar cosas como esta en los valores predeterminados db.

SQLite es bastante fácil de usar y recoger. Tengo un episodio en uno de mis screencasts ( http://pragprog.com/screencasts/v-bdiphone ) sobre un contenedor simple que escribí (puede obtener el código sin comprar el SC).

Es mucho más limpio almacenar datos de la aplicación en el espacio de la aplicación.

Todo lo dicho, si todavía tiene sentido poner estos datos en los valores por defecto db, entonces ver la publicación f3lix publicada.