【问题标题】:Using NSKeyedArchiver/Unarchiver in custom getter/setter methods在自定义 getter/setter 方法中使用 NSKeyedArchiver/Unarchiver
【发布时间】:2013-09-15 11:53:31
【问题描述】:

我正在尝试使用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] 的无限循环调用,所以很明显我做错了什么。但是有什么问题呢?

【问题讨论】:

    标签: objective-c cocoa properties nskeyedarchiver


    【解决方案1】:

    代替:

    self.myDictionary = dictionary;
    

    你应该这样做:

    @synthesize myDictionary;
    ...
    
    -(void)setMyDictionary:(NSDictionary *)dictionary {
    
    ...
    myDictionary = dictionary
    }
    

    您的问题发生原因调用:

    self.myDictionary = dictionary; 
    

    等于

    [self setMyDictionary:dictionary];
    

    【讨论】:

    • +1 是的,但我不建议@synthesize myDictionary;,而是建议@synthesize myDictionary = _myDictionary;,因为使用下划线已成为实例变量支持属性的公认做法。当您打算使用访问器方法时,它有助于最大限度地减少对 ivars 的意外引用。
    • 胜利!当 iOS 6 出现时,我已经停止使用任何类型的 @synthesize,因为我没有看到它在哪里是必要的(而是使用 self.myDictionary_myDictionary)。我刚刚添加了@synthesize myDictionary = _myDictionary,一切似乎都正常。
    【解决方案2】:

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

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

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

      -(void)setMyDictionary:(NSDictionary *)dictionary {
          if (![NSKeyedArchiver archiveRootObject:dictionary toFile:[self pathToDataStore]])
              NSLog(@"%s: archiveRootObject failed", __FUNCTION__);
          _myDictionary = dictionary;
      }
      
    2. 每次您引用self.myDictionary getter 时,它都会从存档中重新检索,即使它已经被检索过。这是低效的,可能会导致一些意想不到的后果(例如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

    【讨论】:

      猜你喜欢
      • 2017-08-23
      • 2020-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-12
      • 1970-01-01
      • 2012-09-09
      • 2017-10-31
      相关资源
      最近更新 更多