如何为 nsoperationqueue 正确使用 autoreleasepool

Posted

技术标签:

【中文标题】如何为 nsoperationqueue 正确使用 autoreleasepool【英文标题】:how to properly use autoreleasepool for an nsoperationqueue 【发布时间】:2010-12-29 20:21:48 【问题描述】:

我有一个正在重构的应用程序,我刚刚实现了多线程,以便 UI 可以运行得更流畅。在 iphone 模拟器中,我没有任何泄漏,但在运行 ios 4.2 的 iPhone 3G 上进行测试时,我得到了内存泄漏。我已经做了很多寻找使用操作队列实现自动释放池的正确方法,我们将不胜感激。

我已经在我的视图控制器中创建了一个 nsoperationqueue

- (void)loadData

  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  NSOperationQueue *queue = [NSOperationQueue new];  // creates multithread for loading data without slowing UI
  NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(firstRun) object:nil];

  [queue addOperation:operation];
  [operation release];
  [queue release];
  [pool release];

【问题讨论】:

【参考方案1】:

首先,您不应该只是创建然后释放队列。将该队列创建为类的 ivars 之一,然后在视图控制器消失时释放它更自然(您也可以取消任何挂起的操作并取消/等待任何正在运行的操作完成)。

其次,创建操作并将其添加到队列中的方法中不需要自动释放池,因为该方法是从主线程调用的。相反,您需要对象实际调用的方法中的自动释放池(这可能在另一个线程上运行)。

因此,您可能有以下内容(假设您将队列命名为 ivar queue_):

- (void)viewDidLoad

  [super viewDidLoad];

  if( !queue_ ) queue_ = [[NSOperationQueue alloc] init];
  // other view loading code


- (void)loadData

  NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(firstRun) object:nil];
  [queue_ addOperation:operation];
  [operation release];


- (void)firstRun

  // Here we may run on another thread, so 1) we need an autorelease pool; and
  // 2) we need to make sure we don't do anything that requires a runloop
  NSAutoreleasePool* threadPool = [NSAutoreleasePool new];

  // do long-running things

  [threadPool drain];


- (void)dealloc

  if( queue_ ) 
    [queue_ setSuspended:YES];
    [queue_ cancelAllOperations];
    // you need to decide if you need to handle running operations
    // reasonably, but don't wait here because that may block the
    // main thread
    [queue_ release];
  
  // other dealloc stuff
  [super dealloc];

您也可以按需初始化队列,因此不要在 viewDidLoad 中初始化,而是检查它的存在并在必要时在您要添加操作的任何地方进行初始化。这可能包含在自己的方法调用中,但这里的延迟初始化可能并不是真正必要的,因为队列非常轻量级。

【讨论】:

感谢 Jason,线程池初始化后调用的任何方法会自动使用它吗?我也必须释放线程池。我看到它以 new 开头。 @zambono:是的,它应该是第一行,您应该在方法的最后一行将池排空。 “new”方法只是alloc/init的一个捷径,所以[NSObject new]和[[NSObject alloc] init]是一样的,所以你自己负责。但是,自动释放池有点特别。那里的 drain 方法也会释放池,所以你不必向它发送特定的释放消息(如果你这样做,它只是忽略它)。【参考方案2】:

您应该在 NSOperation 将调用的方法开始处创建一个NSAutoreleasePool(在本例中为firstRun),并在方法结束时将其排空。

【讨论】:

以上是关于如何为 nsoperationqueue 正确使用 autoreleasepool的主要内容,如果未能解决你的问题,请参考以下文章

如何为 GET 请求正确配置 Apache 重写规则?

如何为可编辑日期设置正确的日期格式

如何为 ReSharper 扩展找到正确版本的“Wave”?

如何为正确的构建系统自动选择 NSColor 和 UIColor? (使用#define,或其他东西)

AWS S3 - 如何为 Android 获取正确的凭证?

如何为 Netlify CMS 正确设置 cloudinary?