Загрузка состояния синглтона из NSKeyedArchiver
-
18-09-2019 - |
Вопрос
У меня есть класс, который я превратил в синглтон, и я могу сохранить его состояние с помощью 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, и он не загружает ни одно из сохраненных значений.До того, как я преобразовал его в одноэлементное сохранение, и загрузка работала нормально.
Решение
Вот где я думаю, что это идет не так.Вы знакомы с 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
Здесь у нас есть общий объект, и он использует архив с ключами для сохранения конфигурации, но мы не кодируем сам общий объект.Это позволяет избежать неловкого вопроса о декодировании второго экземпляра одноэлементного класса.
Другие советы
Вам нужно выполнить загрузку в самом синглтоне. Здесь происходит следующее: вы создаете синглтон, присваиваете ему значение 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, вам нужно разобраться с этим в методе инициализации классов:
- (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;
}