Carregando o estado de um Singleton de NSKeyedArchiver
-
18-09-2019 - |
Pergunta
Eu tenho uma classe que eu fiz em um singleton e sou capaz de salvá-lo do estado usando um NSKeyedArchiver, mas não posso envolver minha cabeça em torno de puxá-lo de volta estado fora.
Na função que faz o carregamento eu tenho
Venue *venue = [Venue sharedVenue];
NSData *data = [[NSMutableData alloc] initWithContentsOfFile:[self dataFilePath]];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
venue = [unarchiver decodeObjectForKey:@"Venue"];
[unarchiver finishDecoding];
Com esse código, o que faz retorno decodeObjectForKey? Não pode ser realmente outra instância do local e não está carregando em qualquer um dos valores salvos. Antes que eu convertido para uma economia Singleton e carregamento estava funcionando bem.
Solução
Aqui é onde eu acho que isso está errado. Você está familiarizado com NSCoding e tipicamente adotá-lo, tornando o seu codificável objeto via encodeWithCoder: e initWithCoder: substituições. O que vai fazer tudo isso mais simples é que você ainda pode usar NSCoding e NSCoders sem substituir esses métodos. Você pode codificar o estado do objeto compartilhado sem que codifica a compartilhada obejct si. Isso evita que a questão embaraçosa de decodificar o objeto compartilhado.
Aqui está um exemplo do que eu acho que você poderia fazer:
@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
Aqui temos um objeto compartilhado, e ele usa arquivos com chave de persistir configuração, mas não codificar o próprio objeto compartilhado. Este evitar essa pergunta embaraçosa de decodificar uma segunda instância da classe Singleton.
Outras dicas
Você precisa fazer o carregamento no singleton em si o que está acontecendo aqui é criar o único, você atribuir um lVal ao Singleton, você, em seguida, criar um novo objeto e atribuir novamente a lVal a esse novo objeto sem modificar o singleton . Em outras palavras:
//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];
O que você quer fazer é lidar com isso no sharedVenue. Há um par de maneiras que as pessoas que singletons, por isso não posso ter certeza que você está fazendo, mas vamos supor sharedVenue atualmente é algo como isto:
static Venue *gSharedVenue = nil;
- (Venue *) sharedVenue {
if (!gSharedVenue) {
gSharedVenue = [[Venue alloc] init];
}
return gSharedVenue;
}
Assumindo que é o caso de você querer mudá-lo para carregar o objeto para o apoio global 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;
}
Obviamente, você precisa de algum modo transmitir o caminho real para arquivo objeto arquivado.
EDIT base no comentário:
Ok, se você estiver usando o alloc base singleton você precisa lidar com isso nas aulas método 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;
}