【问题标题】:Loading a Singleton's state from NSKeyedArchiver从 NSKeyedArchiver 加载 Singleton 的状态
【发布时间】: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 的另一个实例,并且它没有加载任何保存的值。在我将其转换为单例保存和加载之前工作正常。

【问题讨论】:

    标签: iphone objective-c cocoa-touch singleton nskeyedarchiver


    【解决方案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。
    猜你喜欢
    • 1970-01-01
    • 2014-04-30
    • 2016-09-30
    • 1970-01-01
    • 2016-06-04
    • 2015-07-05
    • 2021-06-17
    • 1970-01-01
    • 2019-01-05
    相关资源
    最近更新 更多