将自定义对象存储在NSUserDefaults中的NSMutableArray中

我最近一直试图将我的iPhone应用程序的search结果存储在NSUserDefaults集合中。 我也使用这个来保存用户注册信息,但由于某种原因,试图存储自定义位置类的NSMutableArray总是回到空。

我试图将NSMutableArray转换为NSData元素作为这篇文章,但我得到了相同的结果( 可能保存一个整数数组在iPhone上使用NSUserDefaults? )

我试过的代码示例是:

保存:

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

要么

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

要么

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

加载:

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

要么

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

要么

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

在下面的build议下面我也已经在我的对象中实现了NSCoder(忽略NSString临时过度使用):

 #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"]; } 

为了在数组中加载自定义对象,这是我用来抓取数组的:

 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]; } 

您应该检查从用户默认返回的数据不是零,因为我相信从nil unarchive导致崩溃。

归档很简单,使用下面的代码:

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

正如f3lix指出的那样,您需要使自定义对象符合NSCoding协议。 添加类似下面的方法应该可以做到这一点:

 - (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; } 

我认为你的initWithCoder方法中有一个错误,至less在你提供的代码中你没有返回“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 } 

我用

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

 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]; } 

它对我来说是完美的。

带着敬意,

斯特凡

我认为它看起来像你的代码的问题是不保存结果数组。 它的加载数据尝试使用

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

这将返回由该键指定的数组。

请参阅“为什么NSUserDefaults无法在iPhone SDK中保存NSMutableDictionary?”( 为什么NSUserDefaults无法在iPhone SDK中保存NSMutableDictionary? )


如果你想(去)序列化自定义对象,你必须提供函数来(去)序列化数据(NSCoding协议)。 您引用的解决scheme适用于int数组,因为数组不是对象,而是连续的内存块。

我会build议不要试图在默认的数据库中存储这样的东西。

SQLite是相当容易拿起和使用。 我在我的屏幕录像( http://pragprog.com/screencasts/v-bdiphone )中有一段关于我写的一个简单的包装(你可以在不购买SC的情况下获得代码)。

在应用程序空间中存储应用程序数据要干净得多。

所有这一切说,如果它仍然是有道理的,把这个数据放入默认的数据库,然后看postf3lix张贴。