将 NSAutoreleasePool 与 NSURLConnection 结合使用

发布于 2024-12-09 21:43:26 字数 1916 浏览 1 评论 0原文

我正在尝试遵循 XMLPerformance 示例来制作我自己的 xml 解析器。到目前为止,我在使自动释放池工作时遇到了最困难的时期,当我重新创建池时,我就崩溃了。

我将问题范围缩小到这个测试用例:

PoolCrashTest.h

#import <SenTestingKit/SenTestingKit.h>

@interface PoolCrashTest : SenTestCase
{
  @private
  NSURLConnection *connection;
  NSAutoreleasePool *downloadAndParsePool;
  BOOL done;
}

@property (nonatomic, retain) NSURLConnection *connection;
@property (nonatomic, assign) NSAutoreleasePool *downloadAndParsePool;

- (void)downloadAndParse:(NSURL *)url;
@end

PoolCrashTest.m

#import "PoolCrashTest.h"

@implementation PoolCrashTest

@synthesize downloadAndParsePool, connection;

- (void)downloadAndParse:(NSURL *)url {
  done = NO;
  self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];
  NSURLRequest *theRequest = [NSURLRequest requestWithURL:url];
  self.connection = [[NSURLConnection alloc] 
                     initWithRequest:theRequest delegate:self];
  if (connection != nil) {
    do {
      [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode 
                               beforeDate:[NSDate distantFuture]];
    } while (!done);
  }
  self.connection = nil;
  [downloadAndParsePool release];
  self.downloadAndParsePool = nil;
}

#pragma mark NSURLConnection Delegate methods

- (void)connection:(NSURLConnection *)connection 
    didReceiveData:(NSData *)data {
  [downloadAndParsePool drain];

此行后崩溃 ^

  self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];
}

- (void)testPoolCrash
{
  NSURL *dumpURL = [NSURL URLWithString:@"file:///some.xml"];

  [NSThread detachNewThreadSelector:@selector(downloadAndParse:) 
                               toTarget:self withObject:dumpURL];
  sleep(10);
}

@end

有人可以解释如何正确清除线程中运行的 NSURLConnection 委托中的自动释放池吗?

我尝试尽可能地遵循 XMLPerformance...我的目标是使用大部分默认项目设置的 Lion。

I am trying to follow the XMLPerformance example to make an xml parser of my own. So far I'm having the hardest time making autorelease pools work, I get a crash the instant I recreate a pool.

I narrowed down the issue to this test case:

PoolCrashTest.h

#import <SenTestingKit/SenTestingKit.h>

@interface PoolCrashTest : SenTestCase
{
  @private
  NSURLConnection *connection;
  NSAutoreleasePool *downloadAndParsePool;
  BOOL done;
}

@property (nonatomic, retain) NSURLConnection *connection;
@property (nonatomic, assign) NSAutoreleasePool *downloadAndParsePool;

- (void)downloadAndParse:(NSURL *)url;
@end

PoolCrashTest.m

#import "PoolCrashTest.h"

@implementation PoolCrashTest

@synthesize downloadAndParsePool, connection;

- (void)downloadAndParse:(NSURL *)url {
  done = NO;
  self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];
  NSURLRequest *theRequest = [NSURLRequest requestWithURL:url];
  self.connection = [[NSURLConnection alloc] 
                     initWithRequest:theRequest delegate:self];
  if (connection != nil) {
    do {
      [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode 
                               beforeDate:[NSDate distantFuture]];
    } while (!done);
  }
  self.connection = nil;
  [downloadAndParsePool release];
  self.downloadAndParsePool = nil;
}

#pragma mark NSURLConnection Delegate methods

- (void)connection:(NSURLConnection *)connection 
    didReceiveData:(NSData *)data {
  [downloadAndParsePool drain];

crash after this line ^

  self.downloadAndParsePool = [[NSAutoreleasePool alloc] init];
}

- (void)testPoolCrash
{
  NSURL *dumpURL = [NSURL URLWithString:@"file:///some.xml"];

  [NSThread detachNewThreadSelector:@selector(downloadAndParse:) 
                               toTarget:self withObject:dumpURL];
  sleep(10);
}

@end

Can someone explain how to properly purge autorelease pool in a NSURLConnection delegate running in a thread?

I have tried to follow XMLPerformance as close as possible... I'm targeting Lion with mostly default project settings.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

丑丑阿 2024-12-16 21:43:27

您过度释放了您的池。

我正在重新阅读您的问题以确保我理解,但是如果您查看NSAutoreleasePool 文档 你会看到releasedrain 不应同时使用。 (这与简单地调用 release 两次几乎相同。)仅使用 drain

在垃圾收集环境中,不需要自动释放池。但是,您可以编写一个旨在同时在垃圾收集和引用计数环境中工作的框架。在这种情况下,您可以使用自动释放池向收集器提示收集可能是合适的。在垃圾收集环境中,如有必要,向池发送耗尽消息会触发垃圾收集;然而,释放是一个空操作。在引用计数环境中,耗尽与释放具有相同的效果。 因此,通常情况下,您应该使用排空而不是释放。

编辑: ​​上一个问题可能会帮助您了解如何使用定期排出的 NSAutoreleasePool一个多线程环境。

You're over-releasing your pool.

I'm re-reading your question to make sure I understand, but if you check out the NSAutoreleasePool Documentation you'll see that release and drain should not both be used. (It's nearly identical to simply calling release twice.) Only use drain:

In a garbage-collected environment, there is no need for autorelease pools. You may, however, write a framework that is designed to work in both a garbage-collected and reference-counted environment. In this case, you can use autorelease pools to hint to the collector that collection may be appropriate. In a garbage-collected environment, sending a drain message to a pool triggers garbage collection if necessary; release, however, is a no-op. In a reference-counted environment, drain has the same effect as release. Typically, therefore, you should use drain instead of release.

Edit: This previous question might help you understand how you should use a periodically-drained NSAutoreleasePool in a multi-threaded environment.

罪歌 2024-12-16 21:43:26

当克雷格说你过度释放你的池时,他是正确的。在非GC环境中,releasedrain具有相同的效果。在 GC 环境中,release 对于任何对象都是无操作的,因此必须使用 drain 来代替。我只会使用drain

然而,NSAutoreleasePool 对象并不是真正应该成为类属性的东西;如果您将它们的使用限制在词汇范围内,它们将最适合您。您可以通过多种方式在上面发布的代码中使用池,这就足够了。

请记住,当您旋转运行循环时,它将在通用模式下运行运行循环的调用中弹出或弹出;所以你可以这样做:

if (connection != nil) {
  do {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode 
                             beforeDate:[NSDate distantFuture]];
    [pool drain];
  } while (!done);
}

并且你将耗尽在运行循环的特定回合中创建的任何自动释放的对象。由于运行循环的调用将调用连接的委托回调,因此当该池耗尽时,在委托回调中创建的任何自动释放的对象都将被清除。

如果您对此不满意,您可以在委托方法中放置一个池,具体取决于委托方法可能要做的工作量:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    // Do whatever work you want here
    [pool drain];
}

在您的情况下,它会产生大致相同的效果。

我强烈建议您执行类似于上面第一个示例的操作,并消除您现在拥有的自动释放池行为。将 NSAutoreleasePool 对象保留在单个词法范围内可以方便从调试到必要时安全异常处理的一切。

craig is correct when he says you're over-releasing your pool. In a non-GC environment, release and drain have the same effect. In a GC environment, release is a no-op for any object, so drain must be used instead. I'd just use drain.

However, NSAutoreleasePool objects aren't really things you should be making a property of your class; they'll work best for you if you restrict their use to a lexical scope. There are a couple of ways you could use a pool in the code you posted above which would be sufficient.

Remember when you're spinning your run loop that it's going to be popping in and out of the call to run the run loop in the common modes; so you could do this instead:

if (connection != nil) {
  do {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode 
                             beforeDate:[NSDate distantFuture]];
    [pool drain];
  } while (!done);
}

and you'd be draining any autoreleased objects created in the particular turn of the run loop. Because the connection's delegate callback will be called due to this invocation of the run loop, any autoreleased objects created in the delegate callback will be cleaned up when this pool drains.

If you're not comfortable with this, you could place a pool inside your delegate method depending on how much work your delegate method is likely to be doing:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    // Do whatever work you want here
    [pool drain];
}

And in your case, it would have roughly the same effect.

I would strongly recommend doing something like my first example above and eliminating the autorelease pool behavior you have right now. Keeping NSAutoreleasePool objects to a single lexical scope facilitates everything from debugging to safe exception handling when it becomes necessary.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文