Objective-C 正确复制一个 NSArray?

Posted

技术标签:

【中文标题】Objective-C 正确复制一个 NSArray?【英文标题】:Objective-C copy an NSArray properly? 【发布时间】:2010-02-25 22:53:14 【问题描述】:

我的 iPhone 应用程序不断崩溃,在过去该死的一周里,我已将其范围缩小到这一行:

NSArray *fetchResults = [managedObjectContext executeFetchRequest:request error:&error];

我从上面得到了正确的结果,但是应用程序在访问它后崩溃了 (EXC_BAD_ACCESS)。 如何复制fetchResults 的内容以便我可以使用它?

我试过了

NSArray *retVal = [[NSArray alloc] initWithArray:fetchResults];
NSArray *retVal = [[NSArray alloc] initWithArray:[fetchResults copy]];
NSArray *retVal = [[NSArray alloc] initWithArray:[fetchResults retain]];

但唯一不会让应用崩溃的是

NSArray *retVal = [[NSArray alloc] initWithArray:nil];

有人可以帮我吗?我想我需要学习 Obj-C 内存管理方面的课程。

编辑: 下面是一个更完整的崩溃代码示例:

NSArray *fetchResults = [managedObjectContext executeFetchRequest:request error:&error];
[request release];
NSMutableArray *retVal = [NSMutableArray arrayWithCapacity:0];
for(Job *job in fetchResults)
    //NSLog(@"dev: %@",job.lastmod_device);
    NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
                            [job.jobkey copy], @"entitykey",
                            [NSNumber numberWithInt:[job.lastmod_device timeIntervalSince1970]], @"job_lastmod_device",
                            [NSNumber numberWithInt:[job.lastmod_server timeIntervalSince1970]], @"job_lastmod_server",
                            [NSNumber numberWithInt:[job.customer.lastmod_device timeIntervalSince1970]], @"customer_lastmod_device",
                            [NSNumber numberWithInt:[job.customer.lastmod_server timeIntervalSince1970]], @"customer_lastmod_server",
                            [NSNumber numberWithInt:[job.productionschedule_lastmod_device timeIntervalSince1970]], @"productionschedule_lastmod_device",
                            [NSNumber numberWithInt:[job.productionschedule_lastmod_server timeIntervalSince1970]], @"productionschedule_lastmod_server", nil];
    //NSLog(@"dict: %@", dict);
    [retVal addObject:dict];

return retVal;

以及调用这个方法的代码:

NSArray *arr2 = [self retrieveJobs];

(就是这样;我什至从不使用变量)

编辑 2: 即使只是用一个空的 for 循环遍历获取的结果并且对 fetchResults 对象不做任何其他事情,也会使应用程序崩溃。这怎么可能?

【问题讨论】:

请显示您访问结果的位置。 【参考方案1】:

你在颠簸;这些代码行...

NSArray *retVal = [[NSArray alloc] initWithArray:fetchResults];
NSArray *retVal = [[NSArray alloc] initWithArray:[fetchResults copy]];
NSArray *retVal = [[NSArray alloc] initWithArray:[fetchResults retain]];

后两个是简单的泄漏。第一种是一种复制方式,但retVal = [fetchResults copy]; 是一种更好的复制方式。

但是,当然,您根本不需要副本。那不是问题。你接着说唯一不会崩溃的是空结果集。

这表示两件事之一;您的结果集已损坏(不太可能),或者您访问结果集不正确(可能)。

“抖动”是指您询问了有关崩溃的问题,但没有包括崩溃的回溯或崩溃的位置。添加这些,您可能会很快得到答案。

(不要因为“颠簸”而生气——我们都这样做。即使在使用了 20 多年的 Objective-C 之后,我仍然必须面对掌心,退后一步,仔细考虑才能摆脱颠簸。 )

【讨论】:

【参考方案2】:

NSArray *retVal = [fetchResults retain] 应该为您保留一切。它不会复制,但我希望这不是你真正想要做的。您在那里的第一次尝试应该制作一份副本。如果您不小心,它们都容易泄漏(保证您的第二个示例泄漏)。你确定你没有在程序中做其他事情导致这部分代码失败吗?

如果你想要,这里有一些选项可以做一个真实的副本:

NSArray *retVal = [fetchResults copy];
NSArray *retVal = [[NSArray alloc] initWithArray:fetchResults];

这两个都将保留的数组返回给您。

【讨论】:

【参考方案3】:

您确定提取请求正在返回数据吗?根据文档:

满足从接收器和与接收器的持久存储协调器关联的持久存储中获取的请求指定的条件的对象数组。如果发生错误,则返回 nil。如果没有对象符合请求指定的条件,则返回一个空数组。

另外,您能否显示访问数组的代码?是否在执行 fetch 请求的同一方法中?

【讨论】:

【参考方案4】:
NSArray *retVal = [[NSArray alloc] initWithArray:fetchResults];
NSArray *retVal = [[NSArray alloc] initWithArray:[fetchResults copy]];
NSArray *retVal = [[NSArray alloc] initWithArray:[fetchResults retain]];

您确定 fetchResults 包含结果吗? 因为“fetchResults”对象本身可能会被释放并指向某个垃圾位置。这就是为什么你会崩溃。检查并告知 fetchResults 是否为有效对象。

【讨论】:

【参考方案5】:

要获得不参考原件的真实副本,您需要进行深度复制。一种方法是归档数组并将其解压缩回 NSArray。

Collections with ios

【讨论】:

以上是关于Objective-C 正确复制一个 NSArray?的主要内容,如果未能解决你的问题,请参考以下文章

Objective-c:将目录复制到另一个

具有默认参数的Objective-C函数? [复制]

@dynamic 在 Objective-C 中做了啥? [复制]

我是不是需要发布复制的 NSObjects - Objective-c

Objective-C:将图像从文件复制到设备粘贴板

更具体的通用 Objective-C 内存管理