【问题标题】:When do I release this block?我什么时候释放这个块?
【发布时间】:2012-06-12 01:44:11
【问题描述】:

我正在查看此线程How do you trigger a block after a delay, like -performSelector:withObject:afterDelay:? 中的一些代码。我想知道,如果该块异步执行某些操作,该块应该何时释放?

假设我的代码如下所示:

- (void)testMethod:(id)parameter
{
    dispatch_block_t block = ^{
         SomeAsyncTask *t = [SomeAsyncTask withCompletionBlock:^{
                                 [parameter doAction];
                             }];    
    };
    [self performSelector:@selector(executeBlock:)
            onThread:backgroundThread
               withObject:block
            waitUntilDone:NO];
    dispatch_release(block); //I can release the block here because performSelector      retains the block
}

- (void)executeBlock:(id)block
{
    block();
}

那么关键是 SomeASyncTask 中的完成块将保留参数以便安全释放块吗?

【问题讨论】:

    标签: objective-c memory-management objective-c-blocks grand-central-dispatch


    【解决方案1】:

    Ken Thomases 的回答是正确的。我想对你们的cmets做一个更详细的回应。

    首先,不清楚您是指performSelector:withObject: 还是performSelector:withObject:afterDelay:,因为performSelector:withObject: 是直接同步调用,所以[self performSelector:@selector(runBlock:) withObject:block_];[self runBlock:block_] 相同。我假设它是performSelector:withObject:afterDelay:,因为performSelector:withObject: 没那么有趣。

    一步一步看。 performSelector:withObject:afterDelay: 保留它的参数,所以你可以在给它块后释放它。 performSelector:... 保留它通过其选择器的性能。所以在runBlock 期间,该块是有效的,因为它仍然被performSelector:... 保留。在块执行期间,它仍然有效(因为它仍然在runBlock的执行中)。 doSomethingAsynchronouslyWithCompletionBlock 如果是异步的,则必须保留其参数。以此类推。

    但你不需要那样看。仔细想一想,你就会意识到,内存管理规则是为了你不用担心其他代码做什么,只需要你本地需要什么

    内存管理规则归结为以下条件:每个函数/方法都希望其参数在被调用时有效(这通常意味着在函数调用期间,因为调用函数在这段时间内没有运行,所以它怎么会变得无效,除非这个函数做了一些间接删除它的事情(比如从字典中删除)?); 并且对函数调用后它的有效期保持多长时间没有任何期望。而已。一切由此而来。

    例如,在您的doWork 中,您只关心您需要使用该块多长时间。由于performSelector:...之后就不需要了,所以可以放心释放。 performSelector:... 可能对它进行异步处理并不重要;您甚至可能不知道它是异步的(例如,您可能选择了一个未知的方法来动态调用)。关键是,它做什么并不重要。为什么?因为performSelector:... 不会假定参数比您调用它时更有效。因此,如果它需要保留更长时间(确实如此),它必须保留它(但您不需要知道这一点)。只要需要,它就会一直保留它。

    同样,runBlock 可以假设它所提供的参数在其调用期间是有效的。由于它不需要保留它更长时间(它所做的只是调用块),它不需要保留它。该块不会改变这一点。为什么?同样,因为块不假定其参数(包括块本身)在其调用后是有效的,所以runBlock 不需要保证它。如果块调用doSomethingAsynchronouslyWithCompletionBlock,那很好。 doSomethingAsynchronouslyWithCompletionBlock 不假定任何东西在它的调用之外是有效的,所以如果它是真正异步的,它必须将它保留在某个地方。等等

    【讨论】:

    • 感谢非常详细的回复。我已经更新了我的原始帖子以反映代码更改以及我对正在发生的事情的理解。
    【解决方案2】:

    您可以在调用-performSelector:withObject:afterDelay: 后立即释放它。 (我假设您打算使用 after-delay 变体。)像往常一样,您负责内存管理,其他代码负责其内存管理。换句话说,-performSelector:withObject:afterDelay: 必须保留接收者和传入的对象,直到执行选择器之后。

    编辑添加:顺便说一句,您为什么不使用dispatch_after(),如您所链接问题的答案所示?

    【讨论】:

    • 如果我在 performSelector 之后发布,我仍然会遇到同样的问题。 runBlock 方法必须保留块,并且块必须释放自己。
    • 为什么-runBlock: 必须保留该块?重点仍然存在。内存管理应该在本地完成。您无需采用全球视角。只要确保每个部分都做正确的事,而整体做正确的事。在谈论局部变量或参数时,“部分”是一种方法或函数。在谈论实例变量时,它是一个类。
    • 什么不同步? perform-after-delay 与调用者 (+doWork) 不同步,但在延迟之后,选择器 (+runBlock:) 的实际性能与 perform-after-delay 内部同步,并且这些内部得到保证保留参数直到执行选择器之后。这不是你的问题。 Cocoa 的问题是确保接收者和参数在选择器被执行之前一直保留。
    • 没有任何改变。首先,您只编辑了替代代码 sn-p,这一直不清楚,因为它在语法上是无意义的。如果 within 块中有一个函数或方法与 another 块执行异步操作,则 that 负责复制并最终释放其块。传递给-performSelector... 的块将保留足够长的时间。
    • 一个启动异步进程的块本身并不是异步进程完成所必需的。有一个作为完成处理程序传入的 separate 块。该块将(必须)由负责执行异步操作的代码复制。
    【解决方案3】:

    我可能只是尝试传递一个带有参数的数组。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多