Запазване на масив в Plist файл, който съдържа персонализирани обекти iPhone

Имам клас, наречен XYZ, наследяващ от NSObject и масив, който съдържа обекти на XYZ. Трябва да запиша този масив в plist файл в директорията с документи. След като опитах [array writeToFile....], открих, че writeToFile се проваля, тъй като масивът съдържа потребителски обекти от клас XYZ.

Възможното решение за това е да конвертирате обектите в NSData. Какви са различните начини за постигане на това?? Открих, че NSCoding е подходящ, но не мога да разбера как да го използвам. Всяка помощ ще бъде високо оценена.


person Shri    schedule 13.11.2011    source източник


Отговори (5)


Има много добър урок в блога на Ray Wenderlich, който обяснява как да работите с NSCoding.

Просто трябва да внедрите методите *-(void)encodeWithCoder:(NSCoder )encode и *-(id)initWithCoder:(NSCoder )decoder във вашия обект, който зачита протоколът NSCoding като този:

// Header
@interface AnObject: NSObject <NSCoding>

// Implementation
- (void) encodeWithCoder:(NSCoder *)encoder {
    [encoder encodeObject:ivar1 forKey:@"ivar1"];
    [encoder encodeFloat:ivar2 forKey:@"ivar2"];
}

- (id)initWithCoder:(NSCoder *)decoder {
    self = [super initWithCoder:coder];
    ivar1 = [[coder decodeObjectForKey:@"ivar1"] retain];
    ivar2 = [[coder decodeObjectForKey:@"ivar2"] retain];
    return self;
}

Можете също да проверите официалната документация тук.

person Yannick Loriot    schedule 13.11.2011
comment
Получавам изтичане на памет в - (id)initWithCoder:(NSCoder *)декодер на всеки ред. Знаете ли каква може да е възможната причина? (Моите iVar1 и ivar2 са низове) - person Shri; 19.11.2011
comment
Освободили ли сте ги в метода dealloc? - person Yannick Loriot; 19.11.2011
comment
@YannickL.здравейте, искам да попитам тези неща с NSCoding заслужават ли си или по-добре да спрете на NSDictionary за кода, който сте написали по-горе например? Има ли критерии за избор или е въпрос на вкус? Ако е така, какво ще кажете за разходите за ресурси, кое е по-евтино? Може би това е тема за цял въпрос, но е тихо в теоретично пространство, така че ще съм много благодарен да отговорите тук или да редактирате въпроса си. Благодаря. - person Dima Deplov; 18.02.2014
comment
@flinth Мисля, че е по-добре да се използва протоколът NSCoding отколкото NSDictionary по отношение на модела на проектиране. Използването на NSDictionary предполага създаването на нов обект и вие не контролирате сериализацията. За производителност можете да проверите тази връзка: developer.apple.com/library/mac/documentation/Cocoa/Conceptual/. - person Yannick Loriot; 19.02.2014

След внедряването на encodeWithCoder: и initWithCoder: за персонализирания клас, ето как да пишете и четете:

Напишете:

NSError  *error;
NSData* archiveData = [NSKeyedArchiver archivedDataWithRootObject:yourClassInstance];
[archiveData writeToFile:filePath options:NSDataWritingAtomic error:&error];

Прочети:

NSData *archiveData = [NSData dataWithContentsOfFile:filePath];
YourClass *yourClassInstance = (NSArray*)[NSKeyedUnarchiver unarchiveObjectWithData:archiveData];
person zaph    schedule 13.11.2011
comment
За изтриване на определен обект, как да го направите? - person raaz; 14.11.2011

Съобразяване на вашия XYZ клас с NSCoding протоколът наистина е много подходящ за това, което се опитвате да направите. За да направите това, добавете протокола NSCoding към декларацията на интерфейса на вашия клас:

@interface XYZ <NSCoding>

След това трябва да внедрите encodeWithCoder: и initWithCoder: във вашата реализация. „Кодиране с кодер“ означава „вземете текущото състояние на вашия обект и го опаковайте в общ обект, наречен „енкодер“. „Инициализиране с кодер“ означава „предвид вече опакован обект на енкодер, конфигурирайте себе си с данните, пакетирани в енкодера“. И в двата метода „енкодерът“ ще бъде екземпляр на NSCoder, който има доста прост интерфейс за кодиране ("опаковане") и декодиране ("разопаковане") на основни типове.

Ето примерна реализация:

@implementation XYZ
- (id)initWithCoder:(NSCoder*)encoder
{
    self = [self init];

    if (self)
    {
        self.myIntProperty = [encoder decodeIntForKey:@"myInt"];
    }

    return self;
}

– (void)encodeWithCoder:(NSCoder*)decoder
{
    [decoder encodeInt:self.myIntProperty forKey:@"myInt"];
}

@end
person Josh Rosen    schedule 13.11.2011

Трябва да внедрите два метода: -initWithCoder: и -encodeWithCoder:. Прочетете NSCoding документите; те са доста ясни методи. По принцип искате да кодирате/декодирате вашите променливи на екземпляр с ключ, който съответства на името му.

person Jeff Kelley    schedule 13.11.2011

Това е доста право напред. За даден клас Foo:

#import <Foundation/Foundation.h>

@interface Foo : NSObject <NSCoding> {
    NSArray* bar_;
    NSUInteger baz_;
}

@property (nonatomic, retain) NSArray *bar;
@property (nonatomic, assign) NSUInteger baz;

@end

Всичко, което трябва да направите, за да отговаряте на протокола NSCoding, е да приложите съобщенията initWithCoder: и decodeWithCoder:

#import "Foo.h"

@implementation Foo
@synthesize bar = bar_;
@synthesize baz = baz_;

#pragma mark - NSCoding

- (void)encodeWithCoder:(NSCoder *)encoder  {
    [encoder encodeObject:self.bar forKey:@"bar"];
    [encoder encodeInteger:self.baz forKey:@"baz"];
}

- (id)initWithCoder:(NSCoder *)decoder  {
    if ((self = [super init])) {
        self.bar = [decoder decodeObjectForKey:@"bar"];
        self.baz = [decoder decodeIntegerForKey:@"baz"];
    }
    return self;
}

- (void) dealloc {

    [bar_ release];
    [super dealloc];    
}

@end
person Wayne Hartman    schedule 13.11.2011