Iphone 如何将NSMutableArray(包含其他数组)保存到文件

Iphone 如何将NSMutableArray(包含其他数组)保存到文件,iphone,objective-c,cocoa-touch,nscoder,Iphone,Objective C,Cocoa Touch,Nscoder,以前有人问过这个问题,人们对此给出了很好的指导,例如 但是,如果我只是想将一个NSMutableArray(包含另一个NSMutableArray的各种实例)保存到一个文件中,我想知道是否真的需要使用NSCoder?我尝试了此操作,但只收到一条错误消息: -(void)saveLibraryDat { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMas

以前有人问过这个问题,人们对此给出了很好的指导,例如

但是,如果我只是想将一个NSMutableArray(包含另一个NSMutableArray的各种实例)保存到一个文件中,我想知道是否真的需要使用NSCoder?我尝试了此操作,但只收到一条错误消息:

    -(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
)
    [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'
我的错误消息:

    -(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
)
    [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'
所以我想我必须和NSCoder合作,对吗?如果是这样的话,我想知道该怎么做。人们已经解释了如何使用类来实现这一点,但在我的例子中,我有一个NSMutableArray(myLibrary),它包含一个类的各种实例。我必须在这个类和NSMutableArray中实现NSCoder吗

我按如下方式分配我的库:

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

[myLibrary addObject:newNoteBook];
@interface NoteBook : NSObject <NSCoder> { // …
然后添加一个名为NoteBook.m的类的实例,如下所示:

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

[myLibrary addObject:newNoteBook];
@interface NoteBook : NSObject <NSCoder> { // …
那么我应该把NSCoder命令放在哪里呢?只进我的笔记本。我的课?这会自动处理我的图书馆吗

谢谢你的建议


编辑:

所以我更新了代码,但我想最大的问题是我的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。例如,如果我从另一个方法调用同一个log命令,应用程序将崩溃,我会收到以下错误消息:

    -(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
)
    [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设置中做错了什么。。。因为如果我只是用代码创建我的库,一切都会非常顺利。只有从磁盘加载,应用程序才会崩溃


以下是课程设置:

  #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
你的代码被破坏了。“error”变量未初始化且从未设置,因此当您检查它时,您看到的只是随机垃圾数据。如果想知道写入是否成功,请检查
writeToFile:atomically:
的返回值。如果写入成功,则为
YES
;如果写入未成功,则为
NO

然而,NSArray的
writeTo…
方法用于创建plist。如果数组中有非属性列表对象,则该方法不合适,您需要的是归档器。只需执行类似于
[[NSKeyedArchiver archivedDataWithRootObject:myLibrary]writeToFile:writeToFile:filePath原子地:YES]


要使对象正确地符合NSCODE,只需让它们实现
initWithCoder:
encodeWithCoder:
,并在这些方法中,使用存储对象的实例变量(以及将其取出的检索方法).

NSCoder
是一种协议,您的类必须遵守该协议才能归档到数据/文件。在Java中类似于
Serealizabe

向类标题添加一致性,如下所示:

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

[myLibrary addObject:newNoteBook];
@interface NoteBook : NSObject <NSCoder> { // …
我还建议不要使用
-[NSArray writeToFile:atomically://code>,因为在工作中只使用符合属性列表的对象,而不使用符合编码的类。属性列表对象是
NSString
NSData
NSArray
、或
NSDictionary
NSDate
NSNumber
。该列表无法扩展

而是使用
NSKeyedArchiver
/
NSKeyedArchiver
。使用起来几乎一样简单:

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

我认为你应该考虑XML PrStor或JSON,但我承认我不记得NSCoder做了什么,只是我从来没有用它来保存我的NSRabes。Was类似于[array writeToFile:..
writeToFile
只要对象符合plist(即字符串、数组、数字等)就可以工作。若要存储自定义类,您必须使用NSCoder并自行对其进行整理。@Martin Wickman。谢谢,我已尝试实现此功能,但我想我一定是出了完全错误的地方。加载似乎可行,但如果我尝试访问我的变量,应用程序将崩溃…请参阅我编辑的帖子。非常感谢您的帮助!@Martin Wickman:很抱歉有b另一方面,我自己解决了。谢谢!我尝试了
BOOL success=[myLibrary writeToFile:filePath Atomicaly:YES];如果(!success){NSLog(@“保存myLibrary.dat:%@”时出错);}
,但结果是(无)…我想我还没有完全了解检查错误的意思,我恐怕。谢谢,我做了所有这些(包括NSCoder的实现),但我的错误检查仍然得到(null):
BOOL succeed=[[NSKeyedArchivedDataWithRootObject:myLibrary]writeToFile:filePath Atomicaly:YES];如果(!succeed){NSLog(@“保存myLibrary.dat时出错:%@”,则错误);}
好的,我已经尝试实现你建议的所有内容,但是如果我尝试访问我试图保存的NSMutableArray,我认为我的应用程序崩溃了,我仍然做错了什么。请参阅上面我的更新代码。非常感谢你的帮助!好的,我已经尝试实现了你建议的所有内容,但是我认为我的应用程序崩溃了,我仍然做错了什么如果我尝试访问我试图保存的NSMutableArray。请参阅上面我的更新代码。非常感谢您的帮助!