从 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