从 NSKeyedArchiver 加载 Singleton 的状态

Posted

技术标签:

【中文标题】从 NSKeyedArchiver 加载 Singleton 的状态【英文标题】:Loading a Singleton's state from NSKeyedArchiver 【发布时间】:2010-11-12 00:59:18 【问题描述】:

我有一个类,我已将其制成单例,并且能够使用 NSKeyedArchiver 保存它的状态,但我无法将其状态拉回。

在我拥有的加载函数中

Venue *venue = [Venue sharedVenue];
NSData *data = [[NSMutableData alloc] initWithContentsOfFile:[self dataFilePath]];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
venue = [unarchiver decodeObjectForKey:@"Venue"];
[unarchiver finishDecoding];

使用这段代码,decodeObjectForKey 返回什么?它不能真的是 Venue 的另一个实例,并且它没有加载任何保存的值。在我将其转换为单例保存和加载之前工作正常。

【问题讨论】:

【参考方案1】:

您需要在单例本身中进行加载,这里发生的是您创建单例,为单例分配一个 lval,然后创建一个新对象并将 lval 重新分配给该新对象,而无需修改单例.换句话说:

//Set venue to point to singleton
Venue *venue = [Venue sharedVenue];

//Set venue2 to point to singleton
Venue *venue2 = [Venue sharedVenue];

NSData *data = [[NSMutableData alloc] initWithContentsOfFile:[self dataFilePath]];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

//Set venue to unarchived object (does not change the singleton or venue2)
venue = [unarchiver decodeObjectForKey:@"Venue"];
[unarchiver finishDecoding];

您要做的是在 sharedVenue 中处理这个问题。人们做单例有几种方式,所以我不能确定你在做什么,但我们假设 sharedVenue 目前看起来像这样:

static Venue *gSharedVenue = nil;

- (Venue *) sharedVenue 
  if (!gSharedVenue) 
    gSharedVenue = [[Venue alloc] init];
  

  return gSharedVenue;

假设你想改变它以将对象加载到支持单例的全局中:

static Venue *gSharedVenue = nil;

- (Venue *) sharedVenue 
  if (!gSharedVenue) 
    NSData *data = [[NSMutableData alloc] initWithContentsOfFile:[self dataFilePath]];
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
    [data release];

    gSharedVenue = [unarchiver decodeObjectForKey:@"Venue"];
    [unarchiver finishDecoding];
    [unarchiver release];
  

  if (!gSharedVenue) 
    gSharedVenue = [[Venue alloc] init];
  

  return gSharedVenue;

显然您需要以某种方式传达存档目标文件的实际路径。

根据评论进行编辑:

好的,如果你使用基于 alloc 的单例,你需要在类的 init 方法中处理这个问题:

- (id) init 
  self = [super init];

  if (self) 
    NSData *data = [[NSMutableData alloc] initWithContentsOfFile:[self dataFilePath]];
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
    [data release];

    Venue *storedVenue = [unarchiver decodeObjectForKey:@"Venue"];
    [unarchiver finishDecoding];
    [unarchiver release];

    if (storeVenue) 
       [self release];
       self = [storedVenue retain];
    

  

  return self;

【讨论】:

好的,我想我明白了,让我试一试。至于我实现单例的方式,我正在使用这个人的宏,非常棒:cocoawithlove.com/2008/11/… 他使用了不同的模式,你需要在你的单例初始化方法中处理这个。你需要做一件棘手的事情,那就是返回一个非超级的东西,我将你如何做到这一点附加到答案中【参考方案2】:

这就是我认为出错的地方。您熟悉 NSCoding 并且通常通过通过 encodeWithCoder: 和 initWithCoder: 覆盖使您的对象可编码来采用它。使这一切变得更简单的是,您仍然可以使用 NSCoding 和 NSCoders,而无需覆盖这些方法。您可以对共享对象的状态进行编码,而无需对共享对象本身进行编码。这避免了解码共享对象的尴尬问题。

这是我认为你可以做的一个例子:

@implementation MySharedObject
+ (id)sharedInstance 
    static id sharedInstance = nil;
    if (!sharedInstance) 
        sharedInstance = [[MyClass alloc] init];
    


- (id)init 
    if ((self = [super init])) 
        NSData *data = /* probably from user defaults */
        if (data)  // Might not have any initial state.
            NSKeyedUnarchiver *coder = [[[NSKeyedUnarchiver alloc] initForReadingWithData:data] autorelease];
            myBoolean = [coder decodeBoolForKey:@"MyBoolean"];
            myObject = [[coder decodeObjectForKey:@"MyObject"] retain];
            [coder finishDecoding];
        
    
    return self;


- (void)saveState 
    NSMutableData *data = [NSMutableData data];
    NSKeyedArchiver *coder = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease];
    [coder encodeBool:myBoolean forKey:@"MyBoolean"];
    [coder encodeObject:myObject forKey:@"MyObject"];
    [coder finishEncoding]
    // Save the data somewhere, probably user defaults...   

@end

这里我们有一个共享对象,它使用键控存档来持久化配置,但我们不对共享对象本身进行编码。这避免了解码单例类的第二个实例的尴尬问题。

【讨论】:

这个答案对我来说更有意义,因为我可以绕着它转。另一个的 init 似乎是正确的,但有点难以概念化。 很好的答案...仅供参考,以防有人偶然发现这个问题,我相信 init 方法应该使用 NSKeyedUnarchiver。

以上是关于从 NSKeyedArchiver 加载 Singleton 的状态的主要内容,如果未能解决你的问题,请参考以下文章

是否可以使用 NSKeyedArchiver 来存储动画 UIImage?

从 NSKeyedArchiver 中提取 NSString

NSData 从 NSKeyedArchiver 到 NSString

如何在新版本上转换 NSKeyedArchiver 对象

NSKeyedArchiver.unarchiveObject Swift 3 iOS 10

如何使用 NSKeyedArchiver 对自定义类进行编码和解码