Как сохранить NSMutableArray (содержащий другие массивы) в файл - PullRequest
3 голосов
/ 14 мая 2011

Об этом уже спрашивали, и люди давали очень хорошие инструкции о том, как это сделать, например, здесь .

Однако мне было интересно, действительно ли мне нужно работать с NSCoder, если я просто хотел сохранить один NSMutableArray (содержащий различные экземпляры другого NSMutableArray) в файл? Я попробовал это, но получил только сообщение об ошибке:

    -(void)saveLibraryDat {

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 
    NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents directory
    NSString *filePath = [documentsDirectory stringByAppendingPathComponent:@"myLibrary.dat"];
    NSError *error;

    [myLibrary writeToFile:filePath atomically:YES];

    if (error) {
        NSLog(@"There was an error saving myLibrary.dat: %@", error);
    }

}

Мое сообщение об ошибке:

2011-05-13 22:00:47.840 MoleNotes[15437:207] There was an error saving myLibrary.dat: (
    1,
    2
)

Так что я думаю, мне нужно работать с NSCoder, верно? Если так, то мне было интересно, как это сделать. Люди объяснили, как это сделать с классом, но в моем случае у меня есть NSMutableArray (myLibrary), который содержит различные экземпляры класса. Должен ли я реализовать NSCoder в этом классе и NSMutableArray?

Я размещаю свою библиотеку так:

    myLibrary = [[NSMutableArray alloc] init];

А затем добавьте экземпляры класса с именем NoteBook.m следующим образом:

    NoteBook *newNoteBook = [[NoteBook alloc] init];
       newNoteBook.titleName = @"Notes"; // etc.

[myLibrary addObject:newNoteBook];

Так, куда именно я помещаю команды NSCoder? Только в мой класс NoteBook.m? Будет ли это автоматически заботиться о myLibrary?

Спасибо за любые предложения.


EDIT:

Итак, я обновил свой код, но я полагаю, что большая проблема в том, что моя NSMutableArray myLibrary содержит несколько экземпляров настраиваемого класса, который я настроил (он называется notebook). Я настроил NSCoding для этого класса (и всех его переменных), чтобы я мог сохранить его и загрузить.

Теперь мое приложение работает совершенно нормально, если я создаю NSMutableArray в приложении (т.е. когда приложение запускается в первый раз, файл не существует) вместо загрузки его с диска:

    -(void) setupLibrary {

    myLibrary = [[NSMutableArray alloc] init];

    NoteBook *newNoteBook = [[NoteBook alloc] init];

    newNoteBook.titleName = @"Notes"; 
/...

Если я загружаю его с диска, он также работает нормально:

-(void)loadLibraryDat {

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 
    NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents directory
    NSString *filePath = [documentsDirectory stringByAppendingPathComponent:@"myLibrary.dat"];

    myLibrary = [[NSMutableArray alloc] init];  
    myLibrary = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];

    if (!myLibrary) {
        // if it couldn't be loaded from disk create a new one
        NSLog(@"myLibrary.dat empty... set up new one");


        [self setupLibrary];
        } else { NSLog(@"Loading myLibrary.dat successful."); }

    }

Если я запишу все содержимое моей библиотеки после загрузки, все будет в порядке. Например. следующие работы прекрасно работают:

    NSLog(@"%@", [[self.myLibrary objectAtIndex:0] titleName]); 

Однако большая проблема заключается в том, что любой другой метод пытается получить доступ к myLibrary. Например, если я вызову ту же самую команду журнала из другого метода, приложение вылетит, и я получу это сообщение об ошибке:

    [NSCFString objectAtIndex:]: unrecognized selector sent to instance 0x4b38510
2011-05-14 14:09:10.490 Notes[17091:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSCFString objectAtIndex:]: unrecognized selector sent to instance 0x4b38510'

Это звучит так, как будто моя библиотека как-то освобождена, но я не понимаю, почему. Как это могло случиться? У меня такое чувство, что я сделал что-то не так в своей настройке NSCoding ... потому что, если я просто создаю myLibrary в коде, все работает так же чудесно. Только если я загрузлю его с диска, приложение вылетит.


Вот настройка класса:

  #import <Foundation/Foundation.h>

@interface NoteBook : NSObject <NSCoding> {

    NSString *titleName;

    NSString *fileName;
    NSMutableArray *tabTitles;
    NSMutableArray *tabColours;
    NSMutableArray *tabReference;   

}

@property (nonatomic, retain) NSString *titleName;

@property (nonatomic, retain) NSString *fileName;
@property (nonatomic, retain) NSMutableArray *tabTitles;
@property (nonatomic, retain) NSMutableArray *tabColours;
@property (nonatomic, retain) NSMutableArray *tabReference;

-(id)initWithCoder:(NSCoder *)aDecoder;
-(void)encodeWithCoder:(NSCoder *)aCoder;


@end


//
//  NoteBook.m

#import "NoteBook.h"


@implementation NoteBook

@synthesize titleName, fileName, tabTitles, tabColours, tabReference;

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super init];
    if (self) {
        self.titleName = [aDecoder decodeObjectForKey:@"titleName"];
        self.fileName = [aDecoder decodeObjectForKey:@"fileName"];
        self.tabTitles = [aDecoder decodeObjectForKey:@"tabTitles"];
        self.tabColours = [aDecoder decodeObjectForKey:@"tabColours"];
        self.tabReference = [aDecoder decodeObjectForKey:@"tabReference"];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:titleName forKey:@"titleName"];
    [aCoder encodeObject:fileName forKey:@"fileName"];
    [aCoder encodeObject:tabTitles forKey:@"tabTitles"];
    [aCoder encodeObject:tabColours forKey:@"tabColours"];
    [aCoder encodeObject:tabReference forKey:@"tabReference"];
}

@end

EDIT:

Я думаю, что решил это ... Я забыл немного "я" ... которое все испортило и освободило myLibrary:

    self.myLibrary = [NSKeyedUnarchiver
                   unarchiveObjectWithFile:filePath];

if (self.myLibrary == nil) {
    NSLog(@"myLibrary.dat empty... set up new one");
    [self setupLibrary];
} else { NSLog(@"Loading myLibrary.dat successful."); }

Ответы [ 2 ]

5 голосов
/ 14 мая 2011

Ваш код не работает.Переменная «error» неинициализирована и никогда не устанавливается, поэтому при ее проверке вы просто видите случайные данные мусора.Если вы хотите узнать, была ли запись успешной, проверьте возвращаемое значение writeToFile:atomically:.Это будет YES, если запись удалась, и NO, если она не удалась.

Однако методы NSArray writeTo… предназначены для создания списков.Если в вашем массиве есть объекты не из списка свойств, этот метод не подходит, и вам нужен архиватор.Просто сделайте что-то вроде [[NSKeyedArchiver archivedDataWithRootObject:myLibrary] writeToFile:writeToFile:filePath atomically:YES].

Чтобы ваши объекты правильно соответствовали NSCoding, просто попросите их реализовать initWithCoder: и encodeWithCoder:, и в этих методах используйте методы хранения NSCoder хранить переменные экземпляра объекта (и методы извлечения для их возврата).

1 голос
/ 14 мая 2011

NSCoder - это протокол, которому должен соответствовать ваш класс для архивирования в данные / файл. Работает что-то вроде Serealizabe в Java.

Добавьте соответствие заголовку класса следующим образом:

@interface NoteBook : NSObject <NSCoder> { // …

И тогда вы должны реализовать два метода:

-(id)initWithCoder:(NSCoder)decoder;
{
    self = [super initWithCoder:decoder];
    if (self) {
        _someIvar = [decoder decodeObjectForKey:@"someKey"];
        // And more init as needed…
    }
    return self;
}
-(void)encodeWithCoder:(NSCoder)coder;
{
   [super encodeWithCoder:coder];
   [coder encodeObject:_someIvar forKey@"someKey"];
   /// Etc…
}

Я бы также не советовал использовать -[NSArray writeToFile:atomically:], так как при работе только с объектами, совместимыми со списком свойств, а не с классами, совместимыми с кодом. Объектом списка свойств являются NSString, NSData, NSArray или NSDictionary, NSDate и NSNumber. Список не может быть расширен.

Вместо этого используйте NSKeyedArchiver / NSKeyedUnarchiver. Почти так же просто использовать:

if (![NSKeyedArchive archiveRootObject:yourArrat toFile:path]) {
  // It failed.
}
...