简体   繁体   English

可以使用__weak属性将参数传递给块导致内存泄漏吗?

[英]Can using __weak attribute to pass parameter to blocks lead to memory leaks?

In my iOS ARC-enabled code, I need to pass "self" and other objects to a block. 在我支持iOS ARC的代码中,我需要将“self”和其他对象传递给一个块。 More specifically, I need to interact with self and an ASIHTTPRequest object inside the ASIHTTPRequest 's completionBlock . 更具体地说,我需要在ASIHTTPRequestcompletionBlock与self和ASIHTTPRequest对象进行交互。

_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];
}];

In order to avoid the following warning: Capturing "self" strongly in this block will likely lead to a retain cycle . 为了避免以下警告: 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 我修改了我的代码,以便在此帖子后面的块中使用的对象添加__weak属性: 修复警告“在此块中强烈捕获[一个对象]可能会导致保留周期”在启用ARC的代码中

The resulting code is: 结果代码是:

_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). 您不需要使所有内容都变弱,也不想继续引用块中的弱对象(特别是如果块可以在不同的线程中执行)。

Think of it this way. 这样想吧。 When using ARC, objects are reference counted. 使用ARC时,对象将被引用计数。 dealloc is not called on an object until the reference count goes to zero. 在引用计数变为零之前,不会在对象上调用dealloc。 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.

The object will not get dealloc until all strong references are released. 在释放所有强引用之前,该对象不会获得dealloc。 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. 如果在块中使用它,则块会自动创建自己的强引用,以确保只要块存在,对象就会存在。

A __weak reference is a level of indirection that gives you access to an object AS LONG AS THE OBJECT LIVES. __weak引用是一个间接级别,使您可以访问长期作为对象的对象。 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. 基本上,如果将对象分配给__weak指针,则只要该对象处于活动状态,该指针就可以保证为您提供相同的对象。 once the object starts its own dealloc(), it finds all the __weak pointers and sets them to nil. 一旦对象启动它自己的dealloc(),它就会找到所有__weak指针并将它们设置为nil。

So, your _weak pointer will always be in one of two states. 因此,_weak指针将始终处于两种状态之一。 It points to a valid object as long as the object lives, or it is nil when the object has dealloc. 只要对象存在,它就指向一个有效的对象,或者当对象具有dealloc时它指向nil。 You should never access an object through a weak pointer, because the object could dealloc behind your back, leaving you with a bad pointer. 永远不应该通过弱指针访问一个对象,因为对象可以在你的背后释放,留下一个坏指针。

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. 所以,你想要做的是在栈上创建一个__strong引用,这样只要你想要它,对象就会保持活着状态。

In your case... 在你的情况下......

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

This block obviously holds a strong reference to self . 这个区块显然对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.
    }
}];

Hey, we now have a weak self , but... you are still going to get the same problem. 嘿,我们现在有一个弱小的self ,但是......你仍然会遇到同样的问题。 Why? 为什么? Because _request and _operation are instance variables. 因为_request和_operation是实例变量。 If you access an instance variable inside a block, it implicitly creates a strong reference to self . 如果访问块内的实例变量,它会隐式创建对self的强引用。

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. 现在,您可能不应该在“原始”中使用实例变量,但这是一个不同的主题。

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. 通过这些更改,您将拥有一个不再具有对self的强引用的块,并且如果self确实是dealloc,则它会优雅地处理它。

Finally, I wil reiterate, the assignment to strongSelf is necessary to prevent potential problems where the object disappears after checking weakSelf. 最后,我要重申一下,对strongSelf的赋值是必要的,以防止在检查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.

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. 现在,在这种情况下,块是请求的一部分,它是self一部分,因此self deallocs的可能性很小,但我的主要观点是使用self来阻止保留周期,但仍然总是通过一个访问对象强烈的参考 - 弱强的舞蹈。

Your resulting code is safe. 您生成的代码是安全的。 Read Brad Larson's answer here for specifics. 阅读Brad Larson的答案了解具体细节。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM