使用NSUserDefaults保存NSMutableArray不起作用。

7

我有一个名为myWallet的可变数组,其属性声明如下:

@property (nonatomic, strong) NSMutableArray *myWallet;

这个数组正在被对象 Cards 填充。我从另一个视图控制器中创建了一个 Card 的新实例,因此我使用代理方法来将此创建的卡片传递到我的初始视图控制器,即表格视图控制器。以下是代理方法:

- (void)addCardViewController:(AddCardViewController *)sender didCreateCard:(Card *)newCard
{
    // insert a new card

    self.myCard = newCard;
    [self.myWallet addObject:self.myCard];

    [self.tableView reloadData];
}

到目前为止,我可以创建Card的新实例,当这样做时,在我的初始视图控制器(Card Wallet VC)中会出现一个新的表格单元。
我想要的是,当用户使用完应用程序后,在下次运行应用程序时,之前创建的Card实例仍然出现。因此,我使用NSUserDefaults来实现这一点。下面的方法是我如何使用NSUserDefaults进行保存的。
- (void)saveMyWallet
{

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

    [defaults setObject:self.myWallet forKey:@"myWalletArray"];

    [defaults synchronize];
    NSLog(@"I am saved");
}

我在didSelectRowAtIndexPath方法中调用了这个方法。当我运行应用程序,然后创建一个新的Card实例,点击表格单元格时,这会显示在我的日志中。

2012-05-04 14:51:00.900 CardWallet[24998:f803] *** -[NSUserDefaults setObject:forKey:]: Attempt to insert non-property value '("<Card: 0x6aa02f0>")' of class '__NSArrayM'.  
Note that dictionaries and arrays in property lists must also contain only property values.

可能的原因是什么?你能给我建议吗?关于这个问题,有以下几种方式可以解决。
提前感谢。

首先删除数组的属性,然后使用NSLog检查您的数组中有哪些值,使用键进行操作。 - vishiphone
3个回答

4

您的Card类还必须符合NSCoding Protocol,因为只能使用NSUserDefaults存储NSDataNSStringNSArrayNSDictionaryNSNumber

这意味着您需要向您的类中添加两个方法:

- (void)encodeWithCoder:(NSCoder *)enCoder {
    [super encodeWithCoder:enCoder];

    [enCoder encodeObject:instanceVariable forKey:INSTANCEVARIABLE_KEY];

    // Similarly for the other instance variables.
    ....
}

- (id)initWithCoder:(NSCoder *)aDecoder {

   if(self = [super initWithCoder:aDecoder]) {
       self.instanceVariable = [aDecoder decodeObjectForKey:INSTANCEVARIABLE_KEY];

       // similarly for other instance variables
       ....
   }

   return self;
}

在将其存储到NSUserDefaults之前,创建一个NSData NSKeyedArchiver。像这样:
 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
 NSData *yourArrayAsData = [NSKeyedArchiver archivedDataWithRootObject:yourArray];
 [ud setObject:sharesAsData forKey:kA_KEY_FOR_USERDEFAULTS];

1
这还不够,NSUserDefaults 不会自动为你编码它们。你需要自己来做! - unexpectedvalue
问题:在我的卡类中,我有3个实例变量:名称、PIN和积分。那么,这是否意味着我也应该进行3次解码和编码? - Grauzten
然后你需要使用3次encodeObject:forKey:和3次decodeObjectForKey;。如果你的变量不是对象(可能是int或float),那么请使用encodeInt:forKey:decodeIntForKey: - Jonas Schnelli
好的。在这一行[super encodeWithCoder:enCoder];上,出现了一个错误提示:“没有为'NSObject'声明可见的@interface,声明选择器'encodeWithCoder'”。而我已经在Card.h中声明了以下方法: - (void) encodeWithCoder:(NSCoder *)enCoder; - (id) initWithCOder: (NSCoder *)aDecoder; - Grauzten
请注意,要执行相反的操作,可以使用 [NSKeyedUnarchiver unarchiveObjectWithData:data] - devios1

4
@implementation Card

- (void)encodeWithCoder:(NSCoder *)enCoder 
{
    [super encodeWithCoder:enCoder];
    [enCoder encodeObject:self.name forKey:@"CardName"];
    [enCoder encodeInt:self.pin forKey:@"CardPin"];
    [enCoder encodeDouble:self.points forKey:@"CardPoints"];
}

- (id)initWithCoder:(NSCoder *)aDecoder 
{
   if(self = [super initWithCoder:aDecoder]) 
   {
       self.name = [aDecoder decodeObjectForKey:@"CardName"];
       self.pin = [aDecoder decodeIntForKey:@"CardPin"];
       self.points = [aDecoder decodeDoubleForKey:@"CardPoints"];      
   }
   return self;
}

@end

假设 name 是 NSString 类型,pin 是 NSInteger 类型,Points 是 Double 类型,你可以直接在项目中使用上述代码。

0
如果数组 self.myWallet 包含的对象不是属性列表对象,则需要在保存之前自己将其编码为 NSData,并在加载时解码。
查看 问题 的答案。

谢谢!我已经阅读了这个相同的答案,尝试过了,但在运行时仍然存在一个错误。我会尝试上面的另一个建议,您也在那里发表了评论 :) - Grauzten

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接