0

我正在尝试使用NSKeyedArchiver/NSKeyedUnarchiver将类属性的数据存储在文件中,以便在应用程序启动之间保持属性的状态。

我在 MyClass.h 中声明了这样的属性:

@property (nonatomic, weak) NSDictionary *myDictionary;

我在 MyClass.m 中创建了自定义 getter 和 setter 方法,以确保将 NSDictionary 写入磁盘:

-(NSDictionary *)myDictionary {
    NSDictionary *dictionary;

    NSString *path = [self pathToDataStore];
    if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
        dictionary = [NSDictionary dictionary];
    } else {
        NSData *archivedData = [NSData dataWithContentsOfFile:path];
        dictionary = [NSKeyedUnarchiver unarchiveObjectWithData:archivedData];
    }

    return dictionary;
}

-(void)setMyDictionary:(NSDictionary *)dictionary {
    NSString *path = [self pathToDataStore];
    NSDictionary *rootObject = [NSDictionary dictionaryWithDictionary:dictionary];
    [NSKeyedArchiver archiveRootObject:rootObject toFile:path];
    self.myDictionary = dictionary;
}

这导致调用的无限循环[self setMyDictionary],所以很明显我做错了什么。但问题是什么?

4

2 回答 2

3

代替:

self.myDictionary = dictionary;

你应该做:

@synthesize myDictionary;
...

-(void)setMyDictionary:(NSDictionary *)dictionary {

...
myDictionary = dictionary
}

您的问题发生原因调用:

self.myDictionary = dictionary; 

等于

[self setMyDictionary:dictionary];
于 2013-09-15T12:17:22.983 回答
1

碎片的答案解释了为什么self.myDictionary在设置器中使用语法会导致无限递归(因为“点”语法只是再次调用设置器)并指出,相反,您应该在访问器方法中使用实例变量。

话虽如此,还有一些额外的观察。

  1. 您可以收紧现有代码。例如,为什么 setter 会创建一个新的字典来归档?您可以将dictionary传递给 setter 的内容存档(如果存档因任何原因失败,也可以记录一条消息)。

    -(void)setMyDictionary:(NSDictionary *)dictionary {
        if (![NSKeyedArchiver archiveRootObject:dictionary toFile:[self pathToDataStore]])
            NSLog(@"%s: archiveRootObject failed", __FUNCTION__);
        _myDictionary = dictionary;
    }
    
  2. 每次引用self.myDictionarygetter 时,它都会从存档中重新检索,即使它已经被检索过。这是低效的,可能会导致一些意想不到的后果(例如if (self.myDictionary == self.myDictionary)... 会失败)。如果您没有字典,您可能希望将 getter 定义为仅从存档中检索字典:

    - (NSDictionary *)myDictionary {
        if (!_myDictionary) {
            NSString *path = [self pathToDataStore];
            if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
                _myDictionary = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
            }
        }
    
        return _myDictionary;
    }
    
  3. 请注意,因为您已经定义了这两种访问器方法,您可能希望有@synthesize一行明确定义遵循典型下划线约定的 ivar:

    @synthesize myDictionary = _myDictionary;
    
  4. 此外,weak内存限定符的使用很奇怪(如果不是这个类,谁拥有这个对象?)。我建议制作它strong

于 2013-09-15T17:42:14.773 回答