我已经编写了与文本字段关联的这个简单的操作方法。
每次我在文本字段中输入文本时,都会在 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 次搜索后,我在行 (我)。
如果我调试,我会看到查询包含 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 网站下载了一个名为 PDFLinker 的示例(http://developer.apple.com/library/mac/#samplecode/PDFKitLinker2/Introduction/Intro.html#//apple_ref/doc/uid/DTS10003594)。此示例实现 PDFViewer。我的应用程序的代码和这个例子非常相似。对于相同 PDF 的相同搜索操作,我的内存增加了 300/400 MB,而 PDFLinker 增加了 190 MB。显然我的代码有问题。但我正在一点一点地比较它,我不认为我插入了内存泄漏(并且 Instrument 没有给我任何证据)。也许有一些项目范围的设置?
尚未更新
从 64 位更改为 32 位内存消耗降低。 64位和PDFKit肯定有问题。顺便说一句,第二次搜索时仍然是 EXC_BAD_ACCESS
SOLUTION
关键的一点是带有垃圾收集功能的 PDFKit 存在缺陷。
如果我禁用 GC,一切都会正常工作。
我遇到了另一个使我的分析变得复杂的问题:我在项目设置上禁用了 GC,但在目标设置上仍然启用了 GC。所以Apple 的示例PDFLinked2 有效,而我的则无效。
I have written this trivial action method associated to a textfield.
Every time I enter text into a textfield a search in a PDF is performed and PDFView
automatically scroll to selection:
- (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];
}
}
Problem is that after 3 or 4 searches I get EXC_BAD_ACCESS
on row (i).
If I debug I see that query contains an NSCFString
and not an NSString
.
I think it is a memory management problem..but where?
I replicated the same issue inside a trivial testcase:
@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);
}
}
Update:
1) It seems that it happens also if I use asynchronous search (method beginFindString: withOptions) every time I perform second search.
2) I found a similar problem to mine in MacRuby Issue Tracking: http://www.macruby.org/trac/ticket/1029
3) It seems that if I disable temporarily garbage collection it works but memory goes up.
I wrote something like:
[[NSGarbageCollector defaultCollector] disable];
[[NSGarbageCollector defaultCollector] enable];
surrounding search code
Another Update
Very weird thing is that sometimes all works. Than I clean and Rebuild and problem arises again. From a certain point of view is is not 100% reproducible. I suspect a bug in PDFKit or some compiler setting I have to do
Update Again
Dears it seems very crazy. I'd concentrate on testcase which is very trivial and which replicates easily the problem. What's wrong with it? This testcase works only if I disable (by code or by project setting) GC
Another Update
Boys it seems a bug but I downloaded an example called PDFLinker from Apple website (http://developer.apple.com/library/mac/#samplecode/PDFKitLinker2/Introduction/Intro.html#//apple_ref/doc/uid/DTS10003594). This example implements a PDFViewer. Code of my app and this example are quite similars. For the same search action on same PDF, my memory rises at 300/400 MB while PDFLinker rises at 190MB. Clearly there is something wrong in my code. But I am comparing it bit by bit and I don't think I am inserting memory leaks (and Instrument doesn't give me any evidence). Maybe is there some project-wide setting ?
Update Yet
Changing from 64 bit to 32 bit memory consumption lowered. Surely there is a problem with 64bit and PDFKit. BTW still EXC_BAD_ACCESS on second search
SOLUTION
Crucial point is that PDFKit with Garbage collection is bugged.
If I disable GC all works correctly.
I had another issue that had complicated my analysis: I disabled GC on Project Setting but GC remained enabled on Target Settings. So Apple's example PDFLinked2 worked while mine not.
发布评论
评论(5)
我同意您发现了 PDFKit 中的一个错误。
运行测试用例时,我遇到了各种形式的错误(分段错误、选择器不理解等)。将代码包装在 @try/@catch 中并不能防止与此方法相关的所有错误。
我在打印日志消息时也遇到错误。
要解决该错误,我建议您在调用 -findString:fromSelection: 期间禁用 GC,正如您已经发现的那样。
另外,请务必在重新启用 GC 之前从选择中复制感兴趣的值。也不要只是复制选择。
如果您从代码中的多个位置进行搜索,我还建议您提取一个单独的方法来执行搜索。然后,您可以调用该搜索来执行搜索,而无需重复 GC 禁用/启用嵌套。
I agree you have found a bug in PDFKit.
I got various forms of errors (segmentation fault, selector not understood, etc) running your test case. Wrapping the code in @try/@catch doesn't prevent all errors associated with this method.
I also got errors printing the log message.
To work around the bug(s), I suggest you disable GC during your invocation of -findString:fromSelection:, as you've already discovered.
Also, be sure to make copies of the values of interest from selection before re-enabling GC. Don't just copy selection either.
If you conduct searches from multiple places in your code I also suggest you extract a separate method to perform the search. Then you can invoke that one to conduct the searches for you without duplicating the GC disable/enable nesting.
这类事情通常表明您正挂在指向已被销毁的对象的指针上。打开僵尸对象(使用
NSZombieEnabled
)以准确查看您在何时何地访问坏对象。This sort of thing is usually evidence that you're hanging onto a pointer to an object that has been destroyed. Turn on zombie objects (with
NSZombieEnabled
) to see exactly where and when you're accessing a bad object.从您的屏幕截图来看,您似乎没有打开
NSZombie
。可能是它对您没有帮助的原因。开启方法如下:如何在 Xcode 中启用 NSZombie?
您提供的屏幕截图非常有用,但您确实需要 NSZombie 来找出此类错误。好吧,除非它很明显,但它不是从您发布的代码中看出的。
编辑:我读到了您正在使用垃圾收集的评论。我是一名 iOS 开发人员,所以我对 Objective-C 中的垃圾收集的经验非常有限,但据我所知,
NSZombie
在垃圾收集环境中不起作用。我不确定是否可以在垃圾收集环境中获得 EXC_BAD_ACCESS ,除非您创建自己的指针并尝试在不创建对象的情况下调用其方法,并且我不明白您为什么要这样做。
我听说有些框架不能很好地处理垃圾收集,但我不认为 PDFKit 属于其中。无论如何,解决方案可能是不使用垃圾收集。也许您应该向 Apple 提交错误报告。
Judging from your screen shot it doesn't seem like you have
NSZombie
turned on. Probably the reason why it doesn't help you. Here's how you turn it on:How to enable NSZombie in Xcode?
The screenshot you provided was otherwise very useful, but you really need
NSZombie
to figure out this kind of errors. Well, unless it's obvious, which it isn't from the code you posted.EDIT: I read the comment that you're using garbage collection. I'm an iOS developer, so I have very limited experience with garbage collection in Objective-C, but as far as I understand
NSZombie
doesn't work in a garbage collected environment.I'm not sure it should be possible to get EXC_BAD_ACCESS in a garbage collected environment, unless you create your own pointer and try to call methods on it without having created an object and I don't see why you would do that.
I've heard that some frameworks doesn't work well with garbage collection, but I wouldn't think PDFKit was among them. Anyway, the solution might be to not use garbage collection. Perhaps you should file a bug report with Apple.
将
PDFSelection *selection
保留为成员变量,并将其传递到fromSelection:
而不是nil
。PDFDocument
可能会保留返回的PDFSelection
实例以提高性能。keep
PDFSelection *selection
as a member variable and pass it infromSelection:
instead ofnil
.It is possible that
PDFDocument
keeps the returnedPDFSelection
instance to improve the performance.您在使用它之前是否尝试保留 searchview stringvalue 对象?
正如您所说,当您快速键入时会发生这种情况,并且异步调用也会发生这种情况,有可能在您的
query
对象被释放期间,stringValue
指向的对象被释放。指向它,以及您在搜索中使用它的时间。您可以尝试这样的方法来查看问题是否仍然存在:
当然也有可能
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
并且编译器解释
nil
和NULL
不同的是,这可能会在内部导致一些奇怪的行为。Did you try retaining the searchview stringvalue object before using it?
As you say it happens when you type fast and it happens for async calls, it is possible that the object
stringValue
is pointing to is being released between the time yourquery
object is pointing to it, and the time you use it int the search.You could try something like this to see if the problem persists:
Of course there is also the possibility that
document
is relased. How do you declare it? is it a property with retain? Can it be released by the time you are searching?EDIT:
I see that you posted the code with the second parameter as
NULL
, but in your screenshot, this value isnil
.The documentation says you should use
NULL
when you want to start the search from the beginning.http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Reference/QuartzFramework/Classes/PDFDocument_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40003873-RH2-DontLinkElementID_1
And as the compiler interprets
nil
andNULL
differently, this could be leading to some weird behavior internally.