NSKeyedArchiver 和 NSKeyedUnarchiver 与 NSMutableArray
Posted
技术标签:
【中文标题】NSKeyedArchiver 和 NSKeyedUnarchiver 与 NSMutableArray【英文标题】:NSKeyedArchiver and NSKeyedUnarchiver with NSMutableArray 【发布时间】:2012-05-10 15:10:03 【问题描述】:我希望这与我在这里使用 Mutable 数组这一事实无关,但这让我感到困惑,所以如果是这样的话,我不会感到惊讶。
背景:
我创建了一个小型数据库,它本质上是一个包含自定义对象的 NSMutableArray,我们可以将其称为 recordObjects。我设置了数组:
database = [[NSMutableArray alloc] init];
我的自定义对象,称为“recordObject”,包含以下变量和初始化:
NSString *name;
int anInt;
Bool aBool;
我还合成了方法,因此我可以进行如下调用:
aString = [[database objectAtIndex:someIndex] name];
并向我的控制器类添加了方法来添加、删除和选择要显示的单个记录。到目前为止,一切都按预期正常运行。
接下来,我设置了我的 recordObject 类(NSObject 的子类)以使用 NSCoder(通过包含在 @interface 指令中,并在实现文件中添加了以下自定义编码器和解码器方法:
-(void) encodeWithCoder: (NSCoder *) encoder
[encoder encodeObject: name forKey: @"recordName"];
[encoder encodeInt: anInt forKey: @"recordInteger"];
[encoder encodeBool: aBool forKey: @"recordBool"];
-(id) initWithCoder: (NSCoder *) decoder
name = [decoder decodeObjectForKey: @"recordName"];
anInt = [decoder decodeIntForKey: @"recordInteger"];
aBool = [decoder decodeBoolForKey: @"recordBool"];
为了编写文件,我使用了以下内容:
[NSKeyedArchiver archiveRootObject:database toFile:myPath];
当我运行程序时,一切似乎都可以正常工作。为数组中的每个记录调用编码器方法,并将文件写入磁盘。用 TextEdit 打开文件显示数据在那里(虽然对我来说大部分是无法理解的。)
问题:
这是我遇到障碍的地方。
我添加了以下代码来将文件加载到我的数据库数组中:
database = [NSKeyedUnarchiver unarchiveObjectWithFile:myPath];
当我再次运行程序时,这次加载数据库,它似乎可以正常工作。我的第一个测试是使用 NSMutableArray 计数方法:
x = [database count];
结果是 X 填充了文件中正确数量的记录。如果我保存数据库时有 5 条记录,则在下次执行程序时加载数据库后将 X 设置为 5。
现在,大问题来了:
如果我尝试使用任何访问器方法,程序就会崩溃。例如,如果我在加载数据库后尝试使用以下内容:
aString = [[database objectAtIndex:someIndex] name];
程序崩溃并在控制台返回以下错误:
Program received signal: “EXC_BAD_ACCESS”.
sharedlibrary apply-load-rules all
我的解释是,由于某种原因,数据没有正确加载和初始化到数据库数组中,但对于我来说,我无法弄清楚我在哪里出错了。
附带说明一下,我实现的所有内容都来自 Stephen G. Kochan 的《Objective-C 编程》一书
任何想法将不胜感激!
【问题讨论】:
【参考方案1】:对我来说,这听起来像是一个内存管理问题。 EXC_BAD_ACCESS
通常意味着您正在尝试访问已被释放的对象。 unarchiveObjectWithFile:
返回一个自动释放的对象,因此如果您想保留它,就必须保留它,可以使用显式的 retain
或将其分配给保留属性。
【讨论】:
嗯。这可以解释它(此时我的内存管理非常糟糕......仍然试图将我的思想包裹起来。) 基于上面的代码,你看到了吗,或者你能推荐一种保留数据库数组的方法吗?今晚我将再次阅读有关内存管理的章节,看看我是否也能弄清楚,但如果有一个简单的修复,将不胜感激! :)[database release]; database = [[NSKeyedUnarchiver unarchiveObjectWithFile:myPath] retain];
这是一个内存问题,因为您的消息对象未正确初始化。
刚刚注意到您也没有在 initWithCoder:
方法中保留实例变量,您也需要这样做。【参考方案2】:
您似乎没有在 -initWithCoder 中初始化记录对象:
试试这样的:
-(id) initWithCoder: (NSCoder *) decoder
self = [super init];
if (self)
name = [decoder decodeObjectForKey: @"recordName"] copy];
anInt = [decoder decodeIntForKey: @"recordInteger"];
aBool = [decoder decodeBoolForKey: @"recordBool"];
return self;
存档时数据就在那里,但您没有正确取消存档。
【讨论】:
我会试一试并报告。谢谢。 重要的是这个建议以'return self'结尾。你的缺少这一点。不过也需要超级初始化调用。 不好。我什至尝试使用 [[super alloc] init];但仍然给出同样的错误。 发布您的整个归档/取消归档代码以及 myPath 字符串的代码 搞定了。绝对是一个团队的努力......大声笑还必须保留 NSSTring 变量。感谢您的帮助!【参考方案3】:您的代码存在一些问题。
您的initWithCoder:
方法未完全实现。您必须调用[super init]
并返回self
。您还必须copy
或retain
字符串对象,否则它将被自动释放:
- (id)initWithCoder:(NSCoder *)decoder
self = [super init];
if(self)
name = [[decoder decodeObjectForKey: @"recordName"] copy];
anInt = [decoder decodeIntForKey: @"recordInteger"];
aBool = [decoder decodeBoolForKey: @"recordBool"];
return self;
另一个问题是这一行:
database = [NSKeyedUnarchiver unarchiveObjectWithFile:myPath];
没关系,除了两件事:
-
您没有持有对该对象的引用,因此它将是
自动发布。
NSKeyedArchiver
返回一个不可变对象,在本例中是 NSArray
而不是 NSMutableArray
。
你需要这样做:
database = [[NSKeyedUnarchiver unarchiveObjectWithFile:myPath] mutableCopy];
这将保留对象(因为它是副本)并使对象成为NSMutableArray
。
【讨论】:
-copy 不够吗?根据文档,unarchiveObjectWithFile:“返回以前由 NSKeyedArchivier 编码的对象图”在这种情况下,这是一个 NSMutableArray。 不,如果您在NSMutableArray
上调用copy
,那么您将得到一个不可变的NSArray
。不幸的是,这里的文档只讲述了故事的一半。 NSCoder
也会发生同样的事情。如果你使用NSCoder
编码NSMutableString
,那么当你解码它时你会得到一个NSString
。您必须使用 mutableCopy
来获取可变版本。
您关于归档可变对象的观点不正确。归档和取消归档可变对象(例如 NSMutableArray
或 NSMutableString
)不会使其不可变。
像前面的评论一样,取消归档可变对象会创建一个可变对象,我试过了。可能是以前的 SDK 中的一个错误,已被纠正。【参考方案4】:
我在取消归档自定义对象时遇到了同样的问题
self.calTable = [[NSKeyedUnarchiver unarchiveObjectWithFile:calibrationFile] objectForKey:@"calTable"];
根据 Rob 的回答,我改为
self.calTable = [[[NSKeyedUnarchiver unarchiveObjectWithFile:calibrationFile] mutableCopy] objectForKey:@"calTable"];
它修复了所有错误。
【讨论】:
以上是关于NSKeyedArchiver 和 NSKeyedUnarchiver 与 NSMutableArray的主要内容,如果未能解决你的问题,请参考以下文章
NSKeyedArchiver 和 NSKeyedUnarchiver 与 NSMutableArray