如何在GC环境中的自定义NSURLProtocol中避免_NSCFURLProtocolBridge中的引用计数下溢。

11

基本情况是我有一个自定义的NSURLProtocol。在startLoading中,[self client]的类型为:

<_NSCFURLProtocolBridge> {NSURLProtocol, CFURLProtocol}
问题在于在垃圾回收环境中运行此代码。因为我正在编写屏幕保护程序,所以必须使用垃圾回收。但是,_NSCFURLProtocolBridge协议似乎总是会引发以下错误:

malloc: reference count underflow for (memory_id_here), break on auto_refcount_underflow_error to debug

下面是一个示例调试信息:

ScreenSaverEngine[1678:6807] client is <_NSCFURLProtocolBridge 0x20025ab00> {NSURLProtocol 0x200258ec0, CFURLProtocol 0x20029c400}     ScreenSaverEngine(1678,0x102eda000) malloc: reference count underflow for 0x20025ab00, break on auto_refcount_underflow_error to debug.

您可以看到,这个错误出现在 <_NSCFURLProtocolBridge 0x20025ab00> 处。
当我在 auto_refcount_underflow_error 上打断点时,它似乎会在 URLProtocolDidFinishLoading: 中返回堆栈跟踪:
id client = [self client];
...
[client URLProtocolDidFinishLoading:self];

这个问题似乎已经存在一段时间了,但是在网上似乎没有任何答案:

http://lists.apple.com/archives/cocoa-dev/2008/May/msg01272.html http://www.cocoabuilder.com/archive/message/cocoa/2007/12/17/195056

这些列出的bug只在使用垃圾回收环境时才会出现。有什么办法可以解决这个问题而不引起内存问题吗? 我认为这可能与NSURLProtocol下面的CF类型释放不当有关?


堆栈跟踪的图片: http://img.skitch.com/20090711-qbt4s4jq87jk4g68iaawe7h1hg.png - Dave Martorana
6个回答

4

上一届WWDC时,我们与Webkit工程师确认了这个bug。他亲自查看了代码并发现了这个问题,所以希望他们能够修复它。解决方法是在initWithRequest方法中CFRetain客户端。

- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id <NSURLProtocolClient>)client
{
    // work around for NSURLProtocol bug
    // note that this leaks!
    CFRetain(client);

    if (self = [super initWithRequest:request cachedResponse:cachedResponse client:client])
    {
    }

    return self;
}

3
这是在实现 _NSCFURLProtocolBridge 时出现的一个错误。请使用 http://bugreport.apple.com/ 提交错误报告。如果您可以包含此页面的URL,我们将不胜感激(如果您更新此页面并提供Radar#,那也会很感激)。最好能够附上您屏幕保护程序的二进制文件,这将非常有帮助;无需源代码。幸运的是,它不会导致崩溃。不幸的是,它可能会导致泄漏。

1

我通过对客户端进行CFRetain操作来解决了这个问题,并在下一次调用startLoading时再次进行CFRelease操作。

-(void)startLoading 
{
        if ( client ) CFRelease(client);
        client = [self client];
        CFRetain(client);

当然,在finalize中也一样。

-(void)finalize
{
    if ( client ) CFRelease(client);
    [super finalize];
}

clientNSURLProtocol 子类的实例变量。


1

这个错误通常表示一个对象被使用-retain保留,但是使用CFRelease()释放。如果您认为这不可能是您的对象(这并不是一个可怕的想法),那么您应该打开另一个Radar。但是您应该先四处看看,看看是否有一个CF对象您正在使用-retain,而您应该使用CFRetain()

接下来的内容就是瞎猜了...

您可以通过上溯堆栈并查看传递给这些C++方法(或特别是auto_zone_release)的参数来获得一些见解。尝试在gdb中执行此操作,以查看第一个参数中的内容:

p *($esp)

看看能否对传递的对象有任何见解。如果你很幸运,也许这会起作用:

po (id)(*($esp))

Rob - 在GC环境中,-retain调用不是NOOPs吗?我认为这是一个bug,我会打开一个Radar bug报告。谢谢! - Dave Martorana
1
-retain 在 GC 中是一个 NOOP,但 CFRetain() 不是。因此,如果您调用 -retain 然后 CFRelease(),您将得到不平衡的结果。 - Rob Napier

1

这是我一段时间前提交的错误报告:

http://openradar.appspot.com/8087384

可能也值得提交,它已经被重复了,但修复它会很好。

正如Alex所说,一位苹果开发者在我面前查看了源代码,并通过我们提供的示例轻松地找到了问题所在。


这个 bug 被关闭,因为它是 rdar://8070298 的重复,而这个问题在 Lion 中似乎已经被修复了。 - Quinn Taylor

0

在打开对话框过滤器中,有时会出现使用NSURL时出现相同的错误。 对我来说,当我不再需要它时,将其明确设置为nil就足够了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接