【问题标题】:NSUserDefaults with Custom object that has an array of custom objects具有自定义对象数组的自定义对象的 NSUserDefaults
【发布时间】:2012-06-20 14:09:05
【问题描述】:

我有一个对象,我使用 NSKeyedArchiver 将其转换为 NSData,然后将其存储到 NSUserDefaults 中。除了对象拥有的数组元素之外,所有内容都被正确保存。数组中的所有对象都符合 NSCoder 协议(或任何他们所谓的 - 例如self.property = [decoder decodeObjectForKey:@"key"] and [encoder encodeObjectForKey:@"key"]

当我保存对象时,数组的元素仍保留在数组中,但它们的属性本身不会被保存。我确实调用了sycnrhonize 方法,所以这不是问题。

请注意,所有其他时间我保存并加载它是正确的,它只是不保存属于对象的数组的元素。我必须单独保存吗?

“当前状态”NSNumber 没有被保存。目标和目标正在保存中

导入“Level.h”

@implementation Level
@synthesize objective = _objective;
@synthesize isComplete = _isComplete;
@synthesize goldReward = _goldReward;
@synthesize xpReward = _xpReward;
@synthesize missionID = _missionID;
@synthesize currentStatus = _currentStatus;
@synthesize targetName = _targetName;
@synthesize owner = _owner;

-(void)dealloc{

[super dealloc];
  }

-(id)initWithMissionID:(int)number{

if (self = [super init]) {
    self.currentStatus = 0;
    self.isComplete = NO;
    self.missionID = [NSNumber numberWithInt:number];
    [self setUpMisson];
}
return self;
}
-(void)setUpMisson{
if ([self.missionID intValue] == 0) {
    self.xpReward = [NSNumber numberWithInt:100];
    self.goldReward = [NSNumber numberWithInt:100];
    self.objective = [NSNumber numberWithInt:3];
    self.targetName = @"Swordsman";
    CCLOG(@"Gotta kill some swordsmen!");
}
}
-(void)encodeWithCoder:(NSCoder *)encoder{
[encoder encodeObject:self.objective forKey:@"objective"];
[encoder encodeObject:self.isComplete forKey:@"isComplete"];
[encoder encodeObject:self.goldReward forKey:@"goldReward"];
[encoder encodeObject:self.xpReward forKey:@"xpReward"];
[encoder encodeObject:self.missionID forKey:@"missionID"];
[encoder encodeObject:self.currentStatus forKey:@"currentStatus"];
[encoder encodeObject:self.targetName forKey:@"targetName"];
[encoder encodeObject:self.owner forKey:@"owner"];
CCLOG(@"SAVING LEVEL");
}
-(id)initWithCoder:(NSCoder *)decoder{
if (self = [super init]) {
    self.objective = [[decoder decodeObjectForKey:@"objective"]retain];
    self.isComplete = [[decoder decodeObjectForKey:@"isComplete"]retain];
    self.goldReward = [[decoder decodeObjectForKey:@"goldReward"]retain];
    self.xpReward = [[decoder decodeObjectForKey:@"xpReward"]retain];
    self.missionID = [[decoder decodeObjectForKey:@"missionID"]retain];
    self.targetName = [[decoder decodeObjectForKey:@"targetName"]retain];
    self.owner = [[decoder decodeObjectForKey:@"owner"]retain];
    CCLOG(@"LOADING LEVEL");
}
return self;
}
-(void)updateStatusForKill:(AI *)killedTarget{
CCLOG(@"WE KILLED: %@ and OUR GOAL IS: %@",killedTarget.name,self.targetName);

if ([killedTarget.name isEqualToString:self.targetName]) {
    [self setCurrentStatus:[NSNumber numberWithInt:[self.currentStatus intValue]+1]];
    CCLOG(@"Current Status: %i Objective: %i", [self.currentStatus intValue],[self.objective intValue]);
    if ([self.currentStatus intValue] == [self.objective intValue]) {
        [self completeMission];
    }
}
}
-(void)completeMission{
[self.owner setCoins:[NSNumber numberWithInt:[[self.owner coins]intValue] + [self.goldReward intValue]]];
[self.owner setXp:[NSNumber numberWithInt:[[self.owner xp]intValue] + [self.xpReward intValue]]];
CCLOG(@"complete");
[[self.owner missionList]removeObject:self];

}
@end

编辑:“所有者”指的是要保存的对象。我认为这就是问题所在,所以我将其删除并再次测试。

编辑:那什么也没做!

【问题讨论】:

  • 您能否为受影响的类展示您的-encodeObjectForKey:-decodeObjectForKey: 方法,并准确告诉我们哪些属性没有被保存?

标签: objective-c memory nsmutablearray nsuserdefaults nsdata


【解决方案1】:

您所描述的应该“正常工作”。在自定义对象的 encodeWithCoder 方法中,您只需对数组对象进行编码,这应该会导致数组及其内容被编码。

但是,如果数组中的任何对象不支持 NSCoding,则会失败。我的猜测是您的数组中的某些东西(或者它的子或孙)不支持 NSCoding。

【讨论】:

  • 已经检查过了,里面只有一个对象,它确实支持 NSCoding。我会发布我的实现代码,也许问题就在那里
【解决方案2】:

看起来您正在使用-encodeObject:forKey:-decodeObjectForKey:,即使是在不是对象的值上也是如此。例如,在您的 -initWithMissionID: 中,您有:

self.isComplete = NO;

这让我觉得 completeBOOL 属性,但你的 -encodeObject:forKey: 说:

[encoder encodeObject:self.isComplete forKey:@"isComplete"];

我想你可能想打电话给-encodeBool:forKey:,像这样:

[encoder encodeBool:self.isComplete forKey:@"isComplete"];

另一方面,如果问题如此简单,我真的希望得到某种警告。你收到任何警告吗?推断您的其他属性的类型更难,但请查看您的每个属性以解决相同类型的问题。

【讨论】:

  • 实际上我认为在 self.isComplete 行上它说的是[NSNumber numberWithBool:NO],但没有警告。其余的都是 NSNumber 或 NSString
  • 我能想到的唯一将数字重置为 0 的方法是再次调用 initWithNumber 方法,因为这是唯一将其设置为 0 的地方。这可能是某种原因吗?叫什么?
【解决方案3】:

找到了一种解决方法,对每个任务使用唯一 ID 系统,并将每个任务的进度分别保存到 NSUserDefaults 中,然后再次加载。不理想,但它有效。感谢大家的帮助!

【讨论】:

    【解决方案4】:

    我在尝试保存包含属性值和另一个自定义对象的帐户数组时遇到了这个问题。我无法使用您提出的解决方案保存我的数据,因为我任意将帐户添加到数组中,并且为动态添加的帐户提供唯一标识符是没有意义的。我最终嵌套了 NSCoding 协议:

    在我的AccountManager 班级:

    - (void) saveAllAccounts {
        [self deleteAllAccounts]; //Just removes whatever was previously stored there
        [[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:accountArray] forKey:saveAccountsArrayKey];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
    

    在我的Account 班级:

    - (void) encodeWithCoder:(NSCoder *)aCoder {
        [aCoder encodeObject:username forKey:@"username"];
        [aCoder encodeObject:token forKey:@"token"];
        [aCoder encodeObject:specialID forKey:@"special ID"];
        NSData *data = [NSKeyedArchiver archivedDataWithRootObject:deviceArray];
        [[NSUserDefaults standardUserDefaults] setObject:data forKey:@"device array"];
    }
    
    - (id) initWithCoder:(NSCoder *)aDecoder {
        self = [super init];
        if (self != nil) {
            username = [[aDecoder decodeObjectForKey:@"username"] retain];
            token = [[aDecoder decodeObjectForKey:@"token"] retain];
            ecoID = [[aDecoder decodeObjectForKey:@"eco ID"] retain];
            NSData *deviceArrayData = [[NSUserDefaults standardUserDefaults] objectForKey:@"device array"];
            if (deviceArrayData != nil) {
                NSArray *savedArray = [NSKeyedUnarchiver unarchiveObjectWithData: deviceArrayData];
                if (savedArray != nil)
                    deviceArray = [[NSMutableArray alloc] initWithArray:savedArray];
            }   
        }
        return self;
    }
    

    在我的AccountDevice 班级:

    - (void) encodeWithCoder:(NSCoder *)aCoder {
        [aCoder encodeObject:pairingPassword forKey:@"pairing password"];
        [aCoder encodeObject:devicePin forKey:@"device pin"];
    }
    
    - (id) initWithCoder:(NSCoder *)aDecoder {
        self = [super init];
        if (self != nil) {
            password = [[aDecoder decodeObjectForKey:@"password"] retain];
            pin = [[aDecoder decodeObjectForKey:@"pin"] retain];        
        }
        return self;
    }  
    

    它可能有点bug,因为我还没有完成测试,但我的初步测试是成功的,我认为这个概念就在那里。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-02
      • 1970-01-01
      • 2017-08-30
      • 2016-05-10
      • 1970-01-01
      相关资源
      最近更新 更多