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

Cocoa touch 在NSUserDefaults中的NSMutableArray中存储自定义对象,cocoa-touch,Cocoa Touch,我最近一直在尝试将我的iPhone应用程序的搜索结果存储在NSUserDefaults集合中。我还使用它成功地保存了用户注册信息,但由于某些原因,试图存储自定义位置类的NSMutableArray总是返回空 在本文中,我尝试将NSMutableArray转换为NSData元素,但得到了相同的结果() 我尝试过的代码示例有: 保存: [prefs setObject:results forKey:@"lastResults"]; [prefs synchronize]; lastResults

我最近一直在尝试将我的iPhone应用程序的搜索结果存储在NSUserDefaults集合中。我还使用它成功地保存了用户注册信息,但由于某些原因,试图存储自定义位置类的NSMutableArray总是返回空

在本文中,我尝试将NSMutableArray转换为NSData元素,但得到了相同的结果()

我尝试过的代码示例有:

保存:

[prefs setObject:results forKey:@"lastResults"];
[prefs synchronize];
lastResults = (NSMutableArray *)[prefs objectForKey:@"lastResults"];

加载:

[prefs setObject:results forKey:@"lastResults"];
[prefs synchronize];
lastResults = (NSMutableArray *)[prefs objectForKey:@"lastResults"];

在遵循以下建议后,我还在我的对象中实现了NSCoder(忽略NSString及其临时变量的过度使用):

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



如果要(反)序列化自定义对象,则必须提供(反)序列化数据的函数(NSCoding协议)。您提到的解决方案适用于int数组,因为数组不是一个对象,而是一个连续的内存块。

我认为代码的问题似乎是没有保存结果数组。正在加载数据,请尝试使用

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

这将返回键指定的数组。

我建议不要尝试在默认数据库中存储这样的内容

SQLite非常容易获取和使用。在我的一个屏幕广播()中有一集是关于我编写的一个简单包装器的(你不需要购买SC就可以获得代码)

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


所有这些都表明,如果将这些数据放入默认数据库仍然有意义,那么请参阅发布的f3lix帖子。

要在数组中加载自定义对象,我使用以下内容获取数组:

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,因为我认为从nil取消归档会导致崩溃

存档很简单,使用以下代码:

[[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方法中出现了一个错误,至少在提供的代码中,您没有返回“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"];

它对我来说非常适合

关于,


Stefan

我想你是对的,我会研究一下NSCoding(除非你有推荐的链接),对象本身是由NSStrings组成的,所以我想我可以编写自己的序列化程序,并将它们全部放入字符串arrayOk中,所以我实现了NSCode,请参见上文,但是使用NSARCHIVERTS仍然没有什么乐趣谢谢你的建议,但是我真的不想只为一个有几个字符串的对象编写SQL查询(正如我在你的示例中看到的那样)。你在initWithCoder:方法的末尾缺少了一个“返回自我”。添加这一点似乎允许取消归档。您不应该将数组存储到nsuserdefaults中,因为对于nsuserdefaults来说,数组可能会变得太大。它应该使用+(BOOL)archiveRootObject:(id)rootObject toFile:(NSString*)路径存储到文件中。为什么这个答案会获得如此高的票数?@mskw-你是对的,NSUserDefaults不应该用于存储大型数组(应该使用核心数据或原始SQLite),但是将编码对象的小数组存储在那里是完全正确的。在任何情况下,问题是如何做到这一点,这是上面的代码提供的。就选票而言,你的猜测和我的一样好。在过去的四年里,一定有很多人想这么做。@Goodsum-不,这很好。尝试一下,它将返回一个确实是可变的NSMutableArray。它只是通过将项目数组添加到新数组中来启动新数组。@Alberrenshaw-您添加到上面代码中的
和sync
来自何处?这不是NSUserDefaults的实际方法签名。而且,
synchronize
也不像以前那么有用了:斯蒂芬,你是救生员,看看这里;这句话救了我的心“如果(self=[super init]),我得到了错误的访问权限,但你救了我。将属性与retain only一起使用并添加self.variable解决了此问题。
- (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;
}
- (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];
}