使用 ARC 在其自己的完成块中引用 NSOperation 对象

发布于 2025-01-04 14:35:10 字数 550 浏览 6 评论 0原文

我在将一些 NSOperation 代码转换为 ARC 时遇到困难。我的操作对象使用一个完成块,该完成块又包含一个更新主线程上的 UI 的 GCD 块。因为我从操作对象自己的完成块内部引用操作对象,所以我使用 __weak 指针来避免内存泄漏。但是,当我的代码运行时,指针已经设置为 nil。

我已将范围缩小到此代码示例。有人知道我哪里出错了,以及实现这一目标的正确方法吗?

NSOperationSubclass *operation = [[NSOperationSubclass alloc] init];
__weak NSOperationSubclass *weakOperation = operation;

[operation setCompletionBlock:^{
    dispatch_async( dispatch_get_main_queue(), ^{

        // fails the check
        NSAssert( weakOperation != nil, @"pointer is nil" );

        ...
    });
}];

I'm having difficulty converting some NSOperation code to ARC. My operation object uses a completion block, which in turn contains a GCD block that updates the UI on the main thread. Because I reference my operation object from inside its own completion block, I'm using a __weak pointer to avoid a memory leak. However, the pointer is already set to nil by the time my code runs.

I've narrowed it down to this code sample. Anyone know where I went wrong, and the right way to accomplish this?

NSOperationSubclass *operation = [[NSOperationSubclass alloc] init];
__weak NSOperationSubclass *weakOperation = operation;

[operation setCompletionBlock:^{
    dispatch_async( dispatch_get_main_queue(), ^{

        // fails the check
        NSAssert( weakOperation != nil, @"pointer is nil" );

        ...
    });
}];

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

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

发布评论

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

评论(3

不羁少年 2025-01-11 14:35:10

另一种选择是:

NSOperationSubclass *operation = [[NSOperationSubclass alloc] init];
__weak NSOperationSubclass *weakOperation = operation;

[operation setCompletionBlock:^{
    NSOperationSubclass *strongOperation = weakOperation;

    dispatch_async(dispatch_get_main_queue(), ^{
        assert(strongOperation != nil);
        ...
    });
}];

[operationQueue addOperation:operation];

我假设您还将操作对象添加到 NSOperationQueue 中。在这种情况下,队列将保留一个操作。它可能也在完成块的执行期间保留它(尽管我还没有找到有关完成块的官方确认)。

但在完成块内部会创建另一个块。该块将在稍后的某个时间点运行,可能是在 NSOperations 的完成块运行结束之后。发生这种情况时,operation 将被队列释放,并且 weakOperation 将为 nil。但是,如果我们从操作的完成块创建对同一对象的另一个强引用,我们将确保在运行第二个块时 operation 将存在,并避免保留循环,因为我们不捕获 操作 块中的变量。

Apple 在过渡到 ARC 发行说明中提供了此示例 请参阅使用生命周期限定符避免强引用循环部分中的最后一个代码片段。

Another option would be:

NSOperationSubclass *operation = [[NSOperationSubclass alloc] init];
__weak NSOperationSubclass *weakOperation = operation;

[operation setCompletionBlock:^{
    NSOperationSubclass *strongOperation = weakOperation;

    dispatch_async(dispatch_get_main_queue(), ^{
        assert(strongOperation != nil);
        ...
    });
}];

[operationQueue addOperation:operation];

I assume you also add operation object to an NSOperationQueue. In that case, the queue is retaining an operation. It is probably also retaining it during execution of the completion block (although I haven't found official confirmation about the completion block).

But inside you completion block another block is created. That block will be run at some point in time later, possibly after NSOperations's completion block is run to an end. When this happens, operation will be released by the queue and weakOperation will be nil. But if we create another strong reference to the same object from operation's completion block, we'll make sure operation will exist when the second block is run, and avoid retain cycle because we don't capture operation variable by the block.

Apple provides this example in Transitioning to ARC Release Notes see the last code snippet in Use Lifetime Qualifiers to Avoid Strong Reference Cycles section.

草莓酥 2025-01-11 14:35:10

我对此不确定,但正确的方法可能是将 __block 添加到有问题的变量中,然后在块末尾将其设置为 nil 以确保它被释放。 查看此问题。

您的新代码将如下所示:

NSOperationSubclass *operation = [[NSOperationSubclass alloc] init];
__block NSOperationSubclass *weakOperation = operation;

[operation setCompletionBlock:^{
    dispatch_async( dispatch_get_main_queue(), ^{

        // fails the check
        NSAssert( weakOperation != nil, @"pointer is nil" );

        ...
        weakOperation = nil;
    });

}];

I'm not certain about this, but the correct way to do it is possibly to add __block to the variable in question, and then set it to nil at the end of the block to ensure that it is released. See this question.

Your new code would look like this:

NSOperationSubclass *operation = [[NSOperationSubclass alloc] init];
__block NSOperationSubclass *weakOperation = operation;

[operation setCompletionBlock:^{
    dispatch_async( dispatch_get_main_queue(), ^{

        // fails the check
        NSAssert( weakOperation != nil, @"pointer is nil" );

        ...
        weakOperation = nil;
    });

}];
孤独患者 2025-01-11 14:35:10

接受的答案是正确的。但是,从 iOS 8 / Mac OS 10.10 开始,无需弱化操作:

引用自 @completionBlock 上的 NSOperation 文档

在 iOS 8 及更高版本和 OS X v10.10 及更高版本中,在完成块开始执行后,此属性设置为 nil。

另请参阅 Pete Steinberger 的此推文

Accepted answer is correct. However there is no need to weakify operation as of iOS 8 / Mac OS 10.10:

the quote from NSOperation documentation on @completionBlock:

In iOS 8 and later and OS X v10.10 and later, this property is set to nil after the completion block begins executing.

See also this tweet from Pete Steinberger.

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