使用NSAutoreleasePool和NSURLConnection

时间:2011-10-16 23:07:00

标签: objective-c nsurlconnection nsautoreleasepool

我正在尝试按照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 ...我主要以默认项目设置为目标。

2 个答案:

答案 0 :(得分:2)

当他说你过度释放游泳池时,克雷格是正确的。在非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个对象保存到单个词法范围有助于在必要时从调试到安全异常处理。

答案 1 :(得分:1)

你过度释放你的游泳池。

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

  

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

修改 This previous question可能会帮助您了解如何在多线程环境中使用定期耗尽的NSAutoreleasePool