7

我有一个类,我已经把它做成了一个单例,并且能够使用 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 的另一个实例,并且它没有加载任何保存的值。在我将其转换为单例保存和加载之前工作正常。

4

2 回答 2

8

这就是我认为这是错误的地方。您熟悉 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

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

于 2009-07-20T01:35:12.600 回答
3

您需要在单例本身中进行加载,这里发生的是您创建单例,为单例分配一个 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;
}
于 2009-07-18T23:30:10.147 回答