2009-07-18 5 views
7

Tôi có một lớp mà tôi đã tạo thành một singleton và có thể lưu trạng thái của nó bằng cách sử dụng NSKeyedArchiver, nhưng tôi không thể quấn đầu xung quanh kéo trạng thái của nó trở lại.Đang tải trạng thái của Singleton từ NSKeyedArchiver

Trong chức năng mà không tải Tôi có

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

Với mã này, những gì hiện decodeObjectForKey trở lại? Nó không thể thực sự là một ví dụ khác của Địa điểm và nó không tải trong bất kỳ giá trị đã lưu nào. Trước khi tôi chuyển đổi nó thành một tiết kiệm đơn và tải đang làm việc tốt.

Trả lời

8

Đây là nơi tôi nghĩ rằng đây là đi sai . Bạn đã quen thuộc với NSCoding và thường áp dụng nó bằng cách làm cho đối tượng của bạn có thể mã hóa thông qua encodeWithCoder: và initWithCoder: ghi đè. Điều gì sẽ làm cho tất cả điều này đơn giản hơn là bạn vẫn có thể sử dụng NSCoding và NSCoders mà không ghi đè các phương thức đó. Bạn có thể mã hóa trạng thái của đối tượng được chia sẻ mà không cần mã hóa chính nó. Điều này ngăn cản câu hỏi khó giải mã đối tượng được chia sẻ.

Dưới đây là một ví dụ về những gì tôi nghĩ bạn có thể làm:

@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 

Ở đây chúng ta có một đối tượng chia sẻ, và nó sử dụng lưu trữ có khóa để tồn tại cấu hình, nhưng chúng tôi không mã hóa các đối tượng chia sẻ riêng của mình. Điều này tránh được câu hỏi khó xử giải mã một ví dụ thứ hai của lớp singleton.

+0

Câu trả lời này có ý nghĩa hơn một chút với tôi, chỉ vì tôi có thể quấn đầu quanh nó. Của người khác init có vẻ đúng nhưng khó hơn một chút để khái niệm hóa. – rob5408

+0

Câu trả lời tuyệt vời ... chỉ là một FYI trong trường hợp bất cứ ai tình cờ khi câu hỏi này, tôi tin rằng phương pháp init nên sử dụng một NSKeyedUnarchiver. – Bern11

+0

Cảm ơn, đã cập nhật đoạn mã. –

3

Bạn cần thực hiện tải trong bản thân những gì đang diễn ra ở đây là bạn tạo đĩa đơn, bạn gán một giá trị cho singleton, sau đó bạn tạo một đối tượng mới và gán lại giá trị cho đối tượng mới. singleton. Nói cách khác:

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

Điều bạn muốn làm là giải quyết vấn đề này trong sharedVenue. Có một vài cách mọi người làm độc thân, vì vậy tôi không thể chắc chắn những gì bạn đang làm, nhưng cho phép giả định sharedVenue hiện trông giống như sau:

static Venue *gSharedVenue = nil; 

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

    return gSharedVenue; 
} 

Giả sử đó là trường hợp bạn muốn thay đổi nó để tải đối tượng vào bản sao lưu toàn cầu 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; 
} 

Rõ ràng bạn cần truyền đạt đường dẫn thực tế đến tệp đối tượng đã lưu trữ.

EDIT DỰA VÀO BÌNH:

Được rồi, nếu bạn đang sử dụng dựa singleton alloc bạn cần để đối phó với điều này trong các lớp init phương pháp:

- (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; 
} 
+0

Ok, tôi nghĩ tôi hiểu điều đó, hãy để tôi thử. Đối với cách tôi đang thực hiện singleton của tôi, tôi đang sử dụng vĩ mô của anh chàng này là khá tuyệt vời: http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html – rob5408

+0

Ông sử dụng một mẫu khác nhau, bạn cần xử lý điều này trong phương thức init đơn của bạn. Bạn cần phải làm một điều khó khăn, đó là trả lại một điều không siêu, tôi đang phụ thêm cách bạn làm điều này với câu trả lời –