Tại sao NSUserDefaults không thể lưu NSMutableDipedia trong iOS?


145

Tôi muốn lưu một NSMutableDictionaryđối tượng NSUserDefaults. Loại khóa trong NSMutableDictionaryNSString, loại giá trị là NSArray, chứa một danh sách các đối tượng thực hiện NSCoding. Mỗi tài liệu, NSStringNSArraycả hai đều phù hợp với NSCoding.

Tôi nhận được lỗi này:

[NSUserDefaults setObject: forKey:]: Cố gắng chèn giá trị phi thuộc tính .... của lớp NSCFDixi.

Câu trả lời:


230

Tôi đã tìm ra một thay thế, trước khi lưu, tôi mã hóa đối tượng gốc ( NSArrayđối tượng) bằng cách sử dụng NSKeyedArchiver, kết thúc bằng NSData. Sau đó sử dụng UserDefaults lưu NSData.

Khi tôi cần dữ liệu, tôi đọc NSDatavà sử dụng NSKeyedUnarchiverđể chuyển đổi NSDatatrở lại đối tượng.

Nó hơi cồng kềnh, bởi vì tôi cần chuyển đổi sang / từ NSDatamọi lúc, nhưng nó chỉ hoạt động.

Đây là một ví dụ cho mỗi yêu cầu:

Tiết kiệm:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *arr = ... ; // set value
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arr];
[defaults setObject:data forKey:@"theKey"];
[defaults synchronize];

Tải:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:@"theKey"];
NSArray *arr = [NSKeyedUnarchiver unarchiveObjectWithData:data];

Phần tử trong mảng thực hiện

@interface CommentItem : NSObject<NSCoding> {
    NSString *value;
}

Sau đó, trong việc thực hiện CommentItem, cung cấp hai phương pháp:

-(void)encodeWithCoder:(NSCoder *)encoder
{
    [encoder encodeObject:value forKey:@"Value"];
}

-(id)initWithCoder:(NSCoder *)decoder
{
    self.value = [decoder decodeObjectForKey:@"Value"];
    return self;
}

Bất cứ ai có giải pháp tốt hơn?

Cảm ơn mọi người.


4
Bạn có mẫu mã nào bạn có thể chia sẻ không, tôi đã cố gắng làm điều này trong bài (537044) nhưng không có niềm vui
Anthony Main

Điều này làm việc tốt cho tôi. Tôi đã cố mã hóa một NSArray của các đối tượng NSDipedia trong đó một số khóa không phải là chuỗi. Chỉ cần sử dụng NSKeyedArchiver và Unarchiver trên NSArray đã thoát khỏi lỗi "Cố gắng chèn giá trị phi thuộc tính".
John Wright

Khi nào tôi cần giải phóng đối tượng được tải? Không có phân bổ nào được gọi, vì vậy tôi sẽ cho rằng nó được tự động hóa?
Marc

7
chúng tôi có thể cần thêm [mặc định đồng bộ hóa] để lưu
thanhbinh84

Giải pháp được đăng ở đây có thể hoạt động trong một thời gian, nhưng khi bạn quyết định xóa hoặc thay đổi lớp, bạn sẽ thấy mình bị khóa trong một mớ hỗn độn. Một giải pháp tốt hơn là làm cho đối tượng của bạn viết trạng thái của nó bằng NSNumbers, String, v.v. vào một từ điển, sau đó lưu trữ nó. Bằng chứng tương lai.
Tom Andersen

105

Nếu bạn đang lưu một đối tượng trong mặc định của người dùng, tất cả các đối tượng, theo cách đệ quy, tất cả các đường xuống, phải là các đối tượng danh sách thuộc tính. Tuân thủ NSCoding không có nghĩa gì ở đây-- NSUserDefaultssẽ không tự động mã hóa chúng vào NSData, bạn phải tự mình làm điều đó. Nếu "danh sách đối tượng thực hiện NSCoding" của bạn có nghĩa là các đối tượng không phải là đối tượng danh sách thuộc tính, thì bạn sẽ phải làm gì đó với chúng trước khi lưu vào mặc định của người dùng.

FYI các lớp danh sách tài sản là NSDictionary, NSArray, NSString, NSDate, NSData, và NSNumber. Bạn có thể viết các lớp con có thể thay đổi (như NSMutableDictionary) vào tùy chọn của người dùng nhưng các đối tượng bạn đọc sẽ luôn không thay đổi.


61
Tôi cũng nên đề cập rằng một NSDipedia chỉ là một đối tượng danh sách thuộc tính nếu các khóa là NSStrings. Nói chung, bạn có thể sử dụng các đối tượng khác làm khóa từ điển, nhưng trong đó danh sách thuộc tính được yêu cầu, các khóa phải là chuỗi.
Tom Harrington

Tôi đã gặp một vấn đề tương tự, khi tôi muốn lưu trữ NSURL như một đối tượng trong NSDipedia và lưu trữ nó trong NSUserDefaults. Tôi đã phải thay đổi nó thành một NSString, hơn là nó hoạt động.
NicTesla

1
Tuyệt quá! Trong trường hợp của tôi, tôi đã cố gắng sử dụng NSNumber làm khóa của NSDipedia trong mặc định của người dùng. Tôi phải thay đổi nó thành một NSString.
Joey

1
Hành vi chậm phát triển như vậy là điều gì dẫn đến các lập trình viên thiếu kinh nghiệm viết toàn bộ mô hình dữ liệu của họ dưới dạng từ điển thay vì tạo các đối tượng dữ liệu phù hợp? Làm thế nào khó khăn đến mức Apple có thể viết các lớp nền tảng sử dụng thực tế là obj-c có nội tâm và chăm sóc quyền anh và unboxing cho chúng tôi?
ArtOfWarfare

14

Có phải tất cả các khóa của bạn trong từ điển NSString? Tôi nghĩ rằng họ phải có để lưu từ điển vào danh sách tài sản.


1
các khóa là NSString. nhưng giá trị là NSArray, có phần tử là một lớp tùy chỉnh thực hiện NSCoding. Có vẻ như giải pháp không hoạt động vì UserDefaults chỉ hỗ trợ 6 loại lớp, không xem xét NSCoding.
BlueDolphin

8

Câu trả lời đơn giản nhất:

NSDictionarychỉ là một đối tượng nguyên bản , nếu các khóa là NSStrings . Vì vậy, Lưu trữ "Chìa khóa" như NSStringvới stringWithFormat.


Giải pháp :

NSString *key = [NSString stringWithFormat:@"%@",[dictionary valueForKey:@"Key"]];

Những lợi ích :

  1. Nó sẽ thêm String-Value .
  2. Nó sẽ thêm Giá trị rỗng khi Giá trị biến của bạn là NULL.

2
Tôi đã có một vấn đề tương tự: các khóa của tôi là NSNumbers, dường như không được phép sử dụng trong các khóa của từ điển nguyên bản.
Mark Pauley

5

Bạn đã xem xét việc thực hiện NSCodingNghị định thư chưa? Điều này sẽ cho phép bạn mã hóa và giải mã trên iPhone bằng hai phương pháp đơn giản được thực hiện với NSCoding. Trước tiên, bạn sẽ cần thêm NSCodingLớp học của bạn.

Đây là một ví dụ:

Đây là trong tập tin .h

@interface GameContent : NSObject <NSCoding>

Sau đó, bạn sẽ cần thực hiện hai phương thức của Giao thức NSCoding.

    - (id) initWithCoder: (NSCoder *)coder
    {
        if (self = [super init])
        {
               [self setFoundHotSpots:[coder decodeObjectForKey:@"foundHotSpots"]];
        }
        return self;
    }

    - (void) encodeWithCoder: (NSCoder *)coder
    {
           [coder encodeObject:foundHotSpots forKey:@"foundHotSpots"];
    }

Kiểm tra tài liệu về NSCoder để biết thêm thông tin. Điều đó thực sự hữu ích cho các dự án của tôi, nơi tôi cần lưu trạng thái của ứng dụng trên iPhone nếu ứng dụng bị đóng và khôi phục lại trạng thái khi nó hoạt động trở lại.

Điều quan trọng là thêm giao thức vào giao diện và sau đó thực hiện hai phương thức là một phần của NSCoding .

Tôi hi vọng cái này giúp được!


0

Không có giải pháp nào tốt hơn. Một tùy chọn khác là chỉ lưu đối tượng được mã hóa vào đĩa - nhưng đó là làm điều tương tự. Cả hai đều kết thúc với NSData được giải mã khi bạn muốn nó trở lại.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.