从 NSKeyedArchiver 加载单例状态

发布于 2024-07-27 19:47:29 字数 506 浏览 8 评论 0原文

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

I have a class that I've made into a singleton and am able to save it's state using an NSKeyedArchiver, but I can't wrap my head around pulling it's state back out.

In the function that does the loading I have

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

With this code, what does decodeObjectForKey return? It can't really be another instance of Venue and it's not loading in any of the saved values. Before I converted it to a singleton saving and loading was working fine.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

独自唱情﹋歌 2024-08-03 19:47:29

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

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

Here's where I think this is going wrong. You're familiar with NSCoding and typically adopting it by making your object codable via encodeWithCoder: and initWithCoder: overrides. What will make this all simpler is that you can still use NSCoding, and NSCoders without overriding those methods. You can encode the state of the shared object without encoding the shared obejct itself. This prevents the awkward question of decoding the shared object.

Here's an example of what I think you could do:

@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

Here we have a shared object, and it uses keyed archive to persist configuration, but we don't encode the shared object itself. This avoid that awkward question of decoding a second instance of the singleton class.

美胚控场 2024-08-03 19:47:29

您需要在单例本身中进行加载,这里发生的是您创建单例,为单例分配一个 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当前看起来像这样:

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;
}

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

根据评论进行编辑:

好的,如果您使用基于分配的单例,则需要在类 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;
}

You need to do the loading in the singleton itself what is going on here is you create the single, you assign an lval to the singleton, you then create a new object and reassign the lval to that new object WITHOUT modifying the singleton. In other words:

//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];

What you want to do is deal with this in the sharedVenue. There are a couple of ways people do singletons, so I can't be sure what you are doing, but lets assume sharedVenue currently looks something like this:

static Venue *gSharedVenue = nil;

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

  return gSharedVenue;
}

Assuming that is the case you want to change it to load the object into the global backing the singleton:

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;
}

Obviously you need to somehow convey the actual path to archived object file.

EDIT BASED ON COMMENT:

Okay, if you are using the alloc based singleton you need to deal with this in the classes init method:

- (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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文