【问题标题】:Weird EXC_BAD_ACCESS in a trivial PDFKit program一个简单的 PDFKit 程序中的奇怪 EXC_BAD_ACCESS
【发布时间】:2011-06-11 14:32:54
【问题描述】:

我已经编写了这个与文本字段相关联的简单操作方法。
每次我在文本字段中输入文本时,都会执行 PDF 中的搜索,PDFView 会自动滚动到选择:

- (IBAction) search:(id)id
{
    NSString *query = [self.searchView stringValue]; // get from textfield
    selection = [document findString: query fromSelection:NULL withOptions:NSCaseInsensitiveSearch]; 
    if (selection != nil)
    {
        [self.pdfView setCurrentSelection:selection];
        [self.pdfView scrollSelectionToVisible:self.searchView];
    } 
}

问题是经过 3 或 4 次搜索后,我在第 (i) 行得到 EXC_BAD_ACCESS
如果我调试,我会看到该查询包含 NSCFString 而不是 NSString
我认为这是一个内存管理问题..但是在哪里?

我在一个简单的测试用例中复制了同样的问题:

  @interface PDFRef_protoTests : SenTestCase {
   @private

      PDFDocument *document;

   }

  ........

 - (void)setUp
  {
     [super setUp];
     document = [[PDFDocument alloc] initWithURL: @"a local url ..."];
  }

- (void)test_exc_bad_access_in_pdfdocument
{
    for (int i =0 ;i<100; i++)
    {
        NSString *temp;
        if (i % 2 == 0) temp = @"home";
        else if (i % 3 ==0) temp = @"cocoa";
        else temp=@"apple";
        PDFSelection *selection = [document findString: temp 
                                   fromSelection:nil 
                                withOptions:NSCaseInsensitiveSearch]; 
        NSLog(@"Find=%@, iteration=%d", selection, i);
    }
}

更新

1) 如果我每次执行第二次搜索时都使用异步搜索(方法 beginFindString: withOptions),似乎也会发生这种情况。

2) 我在 MacRuby 问题跟踪中发现了一个与我类似的问题:http://www.macruby.org/trac/ticket/1029

3) 似乎如果我暂时禁用垃圾收集,它会起作用,但内存会增加。 我写了类似的东西:

[[NSGarbageCollector defaultCollector] disable];
[[NSGarbageCollector defaultCollector] enable];

周边搜索代码

另一个更新

非常奇怪的是,有时一切正常。比我清理和重建,问题再次出现。从某种角度来看,并不是 100% 可重现的。我怀疑 PDFKit 中的错误或我必须做的一些编译器设置

再次更新

亲爱的,这似乎很疯狂。我会专注于测试用例,它非常简单并且很容易复制问题。它出什么问题了?此测试用例仅在我禁用(通过代码或通过项目设置)GC 时才有效

另一个更新

男孩,这似乎是一个错误,但我从 Apple 网站 (http://developer.apple.com/library/mac/#samplecode/PDFKitLinker2/Introduction/Intro.html#//apple_ref/doc/uid/DTS10003594) 下载了一个名为 PDFLinker 的示例。这个例子实现了一个 PDFViewer。我的应用程序的代码和这个例子非常相似。对于同一个 PDF 的相同搜索操作,我的内存增加到 300/400 MB,而 PDFLinker 增加到 190MB。显然我的代码有问题。但我正在一点一点地比较它,我认为我没有插入内存泄漏(而且 Instrument 没有给我任何证据)。也许有一些项目范围的设置?

尚未更新 从 64 位更改为 32 位内存消耗降低。 64位和PDFKit肯定有问题。 BTW 在第二次搜索时仍然是 EXC_BAD_ACCESS

解决方案 关键点是带有垃圾收集的 PDFKit 被窃听。 如果我禁用 GC 一切正常。 我遇到了另一个使我的分析变得复杂的问题:我在项目设置上禁用了 GC,但在目标设置上仍然启用了 GC。因此,Apple 的示例 PDFLinked2 有效,而我的无效。

【问题讨论】:

  • 如果我删除垃圾收集器,它似乎可以工作(但可能有内存泄漏)
  • 附注,NSCFStringNSStrings 在内部存储的方式,以允许在 Foundation 和 CoreFoundation 对象之间进行桥接。所以这不是问题或问题。
  • 文档是如何声明/分配的?
  • document是一个在构造函数中初始化的实例变量
  • 您的解决方案看起来与我回答的编辑部分非常相似。然而,我的回答遭到了反对,但从您的解决方案来看,它应该是公认的答案。

标签: objective-c cocoa macruby pdfkit


【解决方案1】:

我同意您在 PDFKit 中发现了一个错误。

我在运行您的测试用例时遇到了各种形式的错误(分段错误、选择器不理解等)。将代码包装在 @try/@catch 中并不能防止与此方法相关的所有错误。

我在打印日志消息时也遇到了错误。

要解决该错误,我建议您在调用 -findString:fromSelection: 期间禁用 GC,正如您已经发现的那样。

此外,请务必在重新启用 GC 之前复制来自选择的感兴趣值。也不要只复制选择

如果您从代码中的多个位置进行搜索,我还建议您提取一个单独的方法来执行搜索。然后你可以调用那个来为你进行搜索,而无需重复 GC 禁用/启用嵌套。

【讨论】:

  • 是的,但正如您所见,有一个可用的苹果演示 (PDFLinker2)。
  • 是的,但正如您所见,PDFLinker2 是为 Tiger 编写的。当我将 PDFLinker2 设置为使用 GC required 针对 10.5 或 10.6 SDK 构建时,它也会导致无法识别的选择器错误和分段错误。是否有办法防止 PDFKit 错误(尤其是 -findString:fromSelection:withOptions:)在 GC 下出现?当然,但谷歌搜索“+PDFKit 垃圾收集”显示了 GC 中 PDFKit 的常见问题。让你的应用对用户来说稳定比让 PDFKit 和 GC 一起工作更重要。要么不要在你的应用中使用 GC,要么在 PDFKit 周围禁用它。
【解决方案2】:

这类事情通常表明您正挂在指向已销毁对象的指针上。打开僵尸对象(使用NSZombieEnabled)以准确查看您在何时何地访问坏对象。

【讨论】:

  • 我会试试的,但是 4 行代码的琐碎混蛋怎么可能成为僵尸对象?
  • 好吧,首先让我们找出哪个对象是坏的。 :)
  • 如果我在文本字段中快速输入并进行一系列搜索(大约 3 或 4 次),我会不断收到 EXC_BAD_ACCESS。有时我收到“线程 1:程序收到的信号:”EXC_BAD_ACCESS”。其他时候我收到 2011-06-11 17:29:24.960 PDFRef_proto[18222:903] -[CPCharacterIndex count]: unrecognized selector sent to instance 0x2002e2200 2011-06 -11 17:29:24.961 PDFRef_proto[18222:903] 处理键输入时检测到异常。2011-06-11 17:29:24.961 PDFRef_proto[18222:903] -[CPCharacterIndex count]:无法识别的选择器发送到实例 0x2002e2200跨度>
  • 再次开启僵尸。你会在 Xcode 控制台中看到哪个对象是坏的。
  • 僵尸没有帮助。它不会在控制台上打印任何有用的东西
【解决方案3】:

从您的屏幕截图来看,您似乎没有打开NSZombie。可能是它对您没有帮助的原因。开启方式如下:

How to enable NSZombie in Xcode?

您提供的屏幕截图在其他方面非常有用,但您确实需要NSZombie 才能找出此类错误。好吧,除非很明显,否则它不是来自您发布的代码。


编辑:我阅读了您正在使用垃圾收集的评论。我是一名 iOS 开发人员,所以我在 Objective-C 中的垃圾收集方面的经验非常有限,但据我了解,NSZombie 在垃圾收集环境中不起作用。

我不确定是否可以在垃圾收集环境中获取 EXC_BAD_ACCESS,除非您创建自己的指针并尝试在未创建对象的情况下调用其上的方法,我不明白您为什么要这样做.

我听说有些框架不能很好地处理垃圾回收,但我认为 PDFKit 不在其中。无论如何,解决方案可能是不使用垃圾收集。也许您应该向 Apple 提交错误报告。

【讨论】:

  • 我在环境变量中添加了 NSZombieEnabled=YES。我看不出有什么区别
  • @pierocampanelli 这没有什么区别,因为您正在使用垃圾收集。我更新了我的答案。
【解决方案4】:

PDFSelection *selection 保留为成员变量并将其传递给fromSelection: 而不是nil

PDFDocument 可能会保留返回的PDFSelection 实例以提高性能。

【讨论】:

  • 文档指出 fromSelection: 可以为 nil。这是因为将其设置为 nil 意味着从 PDF 文档的开头开始搜索。因为我总是想从头开始搜索,所以我无法改变。
  • 但是文档没有提到在你通过一次 nil 后如何重置“从头开始”标志。当然,您的代码应该符合文档和程序员的期望。但是,在您的问题中,您怀疑 PDFKit 中存在错误,在我的回答中,我试图指出错误可能是什么,我认为这值得一试。
  • 它也发生在 fromSelection: argument nil
  • 您是否将要传递给的参数 nil 存储为 ivar?这很重要。
【解决方案5】:

您是否尝试在使用前保留 searchview 字符串值对象?

正如您所说,当您快速键入并且异步调用时会发生这种情况,stringValue 指向的对象可能在您的 query 对象指向它的时间和时间之间被释放你在搜索中使用它。

您可以尝试这样的方法来查看问题是否仍然存在:

- (IBAction) search:(id)id
{
    NSString *query = [[self.searchView stringValue] retain]; // get from textfield
    selection = [document findString: query fromSelection:NULL withOptions:NSCaseInsensitiveSearch]; 
    if (selection != nil)
    {
        [self.pdfView setCurrentSelection:selection];
        [self.pdfView scrollSelectionToVisible:self.searchView];
    } 

    [query release];

}

当然document也有可能被释放。你如何声明它?它是保留的属性吗?可以在您搜索的时候发布吗?

编辑:

我看到您发布的代码的第二个参数为NULL,但在您的屏幕截图中,此值为nil

文档说当你想从头开始搜索时应该使用NULL

http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Reference/QuartzFramework/Classes/PDFDocument_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40003873-RH2-DontLinkElementID_1

并且由于编译器对 nilNULL 的解释不同,这可能在内部导致一些奇怪的行为。

【讨论】:

  • 这绝对是解决方案;事实上,我只是花了几分钟时间输入答案,解释为什么 Piero 应该copy 字符串,然后我才意识到这个问题出现在首页的原因是你刚刚添加了这篇文章。
  • 嗯....我不知道它是否可以修复,我会尝试但很遗憾您的解决方案无法修复测试用例。在这种情况下,您可以看到 NSString 是在方法内部分配的,但我仍然得到了异常
  • 抱歉,这并不能解决我的问题。修复的唯一方法是暂时禁用 GC
  • 文档对象呢?在搜索之间发布是否有任何更改?它被您的控制器正确保留了吗?
  • 这肯定是被设置为自动释放的东西,GC 假定不再使用并释放它。使用 queston 和快照提供的信息,唯一可能适合的变量是查询和文档...
猜你喜欢
  • 2013-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-02
  • 1970-01-01
相关资源
最近更新 更多