可以使用__weak属性将参数传递给块导致内存泄漏吗? [英] Can using __weak attribute to pass parameter to blocks lead to memory leaks?

查看:94
本文介绍了可以使用__weak属性将参数传递给块导致内存泄漏吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我支持iOS ARC的代码中,我需要将self和其他对象传递给一个块。更具体地说,我需要在 ASIHTTPRequest 中与self和 ASIHTTPRequest 对象进行交互。 completionBlock

In my iOS ARC-enabled code, I need to pass "self" and other objects to a block. More specifically, I need to interact with self and an ASIHTTPRequest object inside the ASIHTTPRequest's completionBlock.

_operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(parseServerReply) object:nil];
_request = [ASIHTTPRequest requestWithURL:@"server.address"];

// ...

[_request setCompletionBlock:^{
    [self setResponseString:_request.responseString];
    [[MyAppDelegate getQueue] addOperation:_operation];
}];

为了避免以下警告:在此强烈捕获自我阻止可能会导致保留周期。我修改了我的代码,以便在此帖子之后添加 __ weak 属性块中使用的对象: Fix warning"在这个块中强烈捕获[一个对象]可能会导致到保留周期启用ARC的代码

In order to avoid the following warning: Capturing "self" strongly in this block will likely lead to a retain cycle. I have modified my code to add __weak attributes the the objects that are used in the block following this post: Fix warning "Capturing [an object] strongly in this block is likely to lead to a retain cycle" in ARC-enabled code

结果代码为:

_operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(parseServerReply) object:nil];
_request = [ASIHTTPRequest requestWithURL:@"server.address"];

// ...
__weak NSOperation * operation = _operation;
__weak ASIHTTPRequest * request = _request;
__weak typeof(self) * self_ = self;

[request setCompletionBlock:^{
    [self_ setResponseString:request.responseString];
    [[MyAppDelegate getQueue] addOperation:operation];
}];

我想知道这是否仍会导致保留周期和内存泄漏。如果是这样,有没有办法避免泄漏?

I want to know if this can still lead to retain cycle and memory leaks. If so, is there a way to avoid the leaks?

推荐答案

你不需要让一切都变弱,也不需要继续引用块中的弱对象(特别是如果块可以在不同的线程中执行)。

You don't need to make everything weak, nor do want to keep referencing a weak object in the block (especially if the block can execute in a different thread).

这样想。使用ARC时,对象将被引用计数。在引用计数变为零之前,不会在对象上调用dealloc。因此,只要有一个引用,对象就会保持活跃状态​​。

Think of it this way. When using ARC, objects are reference counted. dealloc is not called on an object until the reference count goes to zero. So, as long as there is one reference around, the object will stay alive.

但是,请考虑两个具有强引用的对象。在另一个发布它的引用之前,它们都不会被释放。

However, consider two objects that have a strong reference to each other. Neither will dealloc until the other releases its reference.

当你创建一个块时,它会捕获它的环境,这意味着它会创建对任何对象的强引用。块的范围。

When you create a block, it captures its environment, and that means it will create strong references to any objects used in the scope of the block.

考虑到这一点......

With that in mind...

id object = getMeSomeObject();
// <object> is implicitly __strong, and now has a reference.

在释放所有强引用之前,对象不会获得dealloc。如果你在一个块中使用它,该块会自动创建它自己的强引用,以确保只要块存在,对象就会存在。

The object will not get dealloc until all strong references are released. If you use it in a block, the block automatically creates its own strong reference to make sure the object lives as long as the block lives.

__weak引用是一个级别间接使您可以访问与对象一样长的对象。基本上,如果将对象分配给__weak指针,则只要该对象处于活动状态,该指针就可以保证为您提供相同的对象。一旦对象启动它自己的dealloc(),它会找到所有的__weak指针并将它们设置为nil。

A __weak reference is a level of indirection that gives you access to an object AS LONG AS THE OBJECT LIVES. Basically, if you assign an object to a __weak pointer, that pointer is guaranteed to give you the same object, as long as that object is alive. once the object starts its own dealloc(), it finds all the __weak pointers and sets them to nil.

所以,你的_weak指针总是处于两种状态之一。只要对象存在,它就指向一个有效的对象,或者当对象具有dealloc时它指向nil。你应该从不通过弱指针访问一个对象,因为对象可以在你的背后释放,留下一个坏指针。

So, your _weak pointer will always be in one of two states. It points to a valid object as long as the object lives, or it is nil when the object has dealloc. You should never access an object through a weak pointer, because the object could dealloc behind your back, leaving you with a bad pointer.

所以,你想要做什么在堆栈上创建一个__strong引用,这样只要你愿意,对象就会保持活着。

So, what you want to do it create a __strong reference on the stack, so that the object stays alive as long as you want it.

在你的情况下...

[_request setCompletionBlock:^{
    [self setResponseString:_request.responseString];
    [[MyAppDelegate getQueue] addOperation:_operation];
}];

这个区块显然强烈引用 self 。你可能不希望这样。让我们试着解决它......

This block obviously holds a strong reference to self. You probably do not want that. Let's try to fix it...

// weakSelf will be "magically" set to nil if <self> deallocs
__weak SelfType *weakSelf = self;
[_request setCompletionBlock:^{
    // If <self> is alive right now, I want to keep it alive while I use it
    // so I need to create a strong reference
    SelfType *strongSelf = weakSelf;
    if (strongSelf) {
        // Ah... <self> is still alive...
        [strongSelf setResponseString:_request.responseString];
        [[MyAppDelegate getQueue] addOperation:_operation];
    } else {
        // Bummer.  <self> dealloc before we could run this code.
    }
}];

嘿,我们现在弱了自我 ,但是......你仍然会遇到同样的问题。为什么?因为_request和_operation是实例变量。如果您访问块内的实例变量,它会隐式创建对 self 的强引用。

Hey, we now have a weak self, but... you are still going to get the same problem. Why? Because _request and _operation are instance variables. If you access an instance variable inside a block, it implicitly creates a strong reference to self.

给它另一个go ...

Giving it another go...

// weakSelf will be "magically" set to nil if <self> deallocs
__weak SelfType *weakSelf = self;
[_request setCompletionBlock:^{
    // If <self> is alive right now, I want to keep it alive while I use it
    // so I need to create a strong reference
    SelfType *strongSelf = weakSelf;
    if (strongSelf) {
        // Ah... <self> is still alive...
        [strongSelf setResponseString:strongSelf->_request.responseString];
        [[MyAppDelegate getQueue] addOperation:strongSelf->_operation];
    } else {
        // Bummer.  <self> dealloc before we could run this code.
    }
}];

现在,您可能不应该在原始中使用实例变量,但这是一个不同的主题。

Now, you probably should not be using instance variables "in the raw" but that's a different topic.

通过这些更改,您有一个不再具有强烈自我引用的块,如果 self 真正dealloc,它优雅地处理它。

With these changes, you have a block that no longer has a strong reference to self, and if self does truly dealloc, it handles it gracefully.

最后,我将重申,为了防止在检查weakSelf后对象消失的潜在问题,必须分配给strongSelf。具体来说......

Finally, I wil reiterate, the assignment to strongSelf is necessary to prevent potential problems where the object disappears after checking weakSelf. Specifically...

if (weakSelf) {
    // Hey, the object exists at the time of the check, but between that check
    // and the very next line, its possible that the object went away.
    // So, to prevent that, you should ALWAYS assign to a temporary strong reference.
    [weakSelf doSomething];
}

strongSelf = weakSelf;
// OK, now IF this object is not nil, it is guaranteed to stay around as long as
// strongSelf lives.

现在,在这种情况下,该块是请求的一部分,该请求是<$ c的一部分$ c> self 所以 self deallocs的可能性很小,但我的主要观点是使用self来阻止保留周期,但仍然总是如此通过强大的参考 - 弱强舞蹈来访问对象。

Now, in this case, the block is part of the request, which is part of self so the likelihood that self deallocs is small, but my main point here is to use self to prevent retain cycles, but still always access the object through a strong reference -- the weak-strong dance.

这篇关于可以使用__weak属性将参数传递给块导致内存泄漏吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆