5

Tôi đang lưu trữ một loạt các dữ liệu trong một file plist (trong thư mục tài liệu ứng dụng), và nó có cấu trúc như thế này:Cách tốt nhất để lưu trữ và truy xuất NSMutableArrays đa chiều là gì?

Dictionary { 
    "description" = "String Value", 
    "sections" = Array (
     Array (
      Number, 
      ... 
      Number 
     ), 
     Array (
      Number, 
      ... 
      Number 
     ) 
    ), 
    "items" = Array (
     Array (
      Number, 
      ... 
      Number 
     ), 
     Array (
      Number, 
      ... 
      Number 
     ) 
    ) 
} 

Nếu tôi chỉ lấy nó với
NSMutableDictionary *d = [[NSMutableDictionary alloc] initWithContentsOfFile:plistFile] tôi sẽ không có thể thay thế các đối tượng số, đúng không? Vì vậy, tôi đang đệ quy thông qua dữ liệu ngay bây giờ và tạo thành một phiên bản có thể thay đổi được, và nó hoạt động trong một trường hợp, nhưng bây giờ nó nói với tôi mutating method sent to immutable object khi toàn bộ điều có thể thay đổi được.

Có cách nào dễ hơn/tốt hơn để thực hiện việc này không? Nếu nó tạo sự khác biệt, dữ liệu của tôi chỉ là số nguyên và boolean.

+0

Bạn có thể cung cấp cho chúng tôi thêm một chút thông tin như đối tượng đang được gửi phương pháp này hay không và có thể đăng mã bạn sử dụng để tạo phiên bản mới và lỗi thời gian chạy được tạo bởi? –

+0

Bạn cũng có tạo bản sao có thể thay đổi của mảng trước khi bắt đầu chỉnh sửa các giá trị không? – vfn

Trả lời

9

Thay vì viết tất cả tùy chỉnh lớp rác, bạn nên sử dụng NSPropertyListSerialization. Cụ thể, hãy xem phương thức propertyListWithData:options:format:error:. Ví dụ về cách sử dụng:

NSMutableDictionary *d = [NSPropertyListSerialization propertyListWithData:[NSData dataWithContentsOfFile:@"path/to/file"] 
                    options:NSPropertyListMutableContainers 
                    format:NULL 
                    error:NULL]; 

Điều này sẽ làm cho tất cả các thùng chứa có thể thay đổi, nhưng giữ nút lá (ví dụ: NSStrings) không thay đổi. Ngoài ra còn có một tùy chọn để làm cho lá cũng biến đổi được.

+0

+1 Điều đó thật hấp dẫn.Tài liệu của Apple nói rằng tham số 'options' không được sử dụng khi đọc (như bạn hiển thị ở đây), nhưng thực tế nó có thể được sử dụng khi * viết * tệp plist, để dữ liệu, khi đọc, có thể thay đổi được. Xem http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSPropertyListSerialization_Class/Reference/Reference.html –

+0

Tôi vẫn sẽ tranh luận cho một lớp tùy chỉnh để nhận được các lợi ích của OO về tài sản accessors và để đóng gói các phương thức đọc và ghi, nhưng kỹ thuật này chắc chắn sẽ giúp cho những dịp đó khi một đơn giản đọc-sửa đổi-viết là theo thứ tự. Công cụ tuyệt vời! –

+0

Vâng, tôi đã đi tuyến đường tùy chỉnh để thuận tiện, nhưng điều này có vẻ khá đơn giản. Cảm ơn các đầu vào. – kbanman

8

Tôi thường thấy dễ dàng hơn khi tạo một hoặc nhiều lớp tùy chỉnh để xử lý tải và lưu. Điều này cho phép bạn chuyển đổi các mảng để mutableArrays một cách rõ ràng:

MyThing.h

@interface MyThing : NSObject 
{ 
    NSString * description; 
    NSMutableArray * sections; 
    NSMutableArray * items; 
} 
@property (copy) NSString * description; 
@property (readonly) NSMutableArray * sections; 
@property (readonly) NSMutableArray * items; 
- (void)loadFromFile:(NSString *)path; 
- (void)saveToFile:(NSString *)path; 
@end 

MyThing.m

@implementation MyThing 
@synthesize description; 
@synthesize sections 
@synthesize items; 

- (id)init { 
    if ((self = [super init]) == nil) { return nil; } 
    sections = [[NSMutableArray alloc] initWithCapacity:0]; 
    items = [[NSMutableArray alloc] initWithCapacity:0]; 
    return self; 
} 

- (void)dealloc { 
    [items release]; 
    [sections release]; 
} 

- (void)loadFromFile:(NSString *)path { 
    NSDictionary * dict = [NSDictionary dictionaryWithContentsOfFile:path]; 
    [self setDescription:[dict objectForKey:@"description"]]; 
    [sections removeAllObjects]; 
    [sections addObjectsFromArray:[dict objectForKey:@"sections"]]; 
    [items removeAllObjects]; 
    [items addObjectsFromArray:[dict objectForKey:@"items"]]; 
} 

- (void)saveToFile:(NSString *)path { 
    NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys: 
          description, @"description", 
          sections, @"sections", 
          items, @"items", 
          nil]; 
    [dict writeToFile:path atomically:YES]; 
} 

@end; 

Với điều đó được thực hiện, bạn có thể gói gọn tất cả mã đóng gói và đóng gói trong các phương thức loadFromFilesaveToFile của mình. Lợi ích chủ yếu của phương pháp này là chương trình chính của bạn trở nên đơn giản hơn rất nhiều, và nó cho phép bạn truy cập vào các yếu tố của cấu trúc dữ liệu của bạn như tài sản:

MyThing * thing = [[MyThing alloc] init]; 
[thing loadFromFile:@"..."]; 
... 
thing.description = @"new description"; 
[thing.sections addObject:someObject]; 
[thing.items removeObjectAtIndex:4]; 
... 
[thing saveToFile:@"..."]; 
[thing release]; 
+0

Wow, đó là câu trả lời khá kỹ lưỡng, thưa bạn :) Điều đó có ý nghĩa rất nhiều Tôi không biết tại sao nó không vượt qua tâm trí của tôi. Cảm ơn bạn! – kbanman

1

gì bạn muốn là một bản sao có thể thay đổi sâu sắc. Ca cao không bao gồm một cách để làm điều đó. Một số ít người đã viết các bản sao chép sâu như vậy trước đây (example).

Tuy nhiên, Core Foundation bao gồm CFPropertyList API, không hỗ trợ cả việc tạo bản sao có thể thay đổi sâu của đối tượng danh sách thuộc tính cũng như đọc trong danh sách thuộc tính từ đĩa dưới dạng kiểu dữ liệu có thể thay đổi. (Và, tất nhiên, các loại danh sách tài sản của Core Foundation là cầu nối miễn phí với Cocoa, nghĩa là bạn không phải chuyển đổi giữa chúng - một NSArray là một CFArray và ngược lại.)

Các vấn đề liên quan