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

gfr*_*gon 4 iphone ios objective-c-blocks ios5

在我支持iOS ARC的代码中,我需要将"self"和其他对象传递给一个块.更具体地讲,我需要与自我和互动ASIHTTPRequest内部对象ASIHTTPRequestcompletionBlock.

_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];
}];
Run Code Online (Sandbox Code Playgroud)

为了避免以下警告:Capturing "self" strongly in this block will likely lead to a retain cycle.我已经修改了我的代码,以便__weak在此帖子之后添加属性块中使用的对象:修复警告"在此块中强烈捕获[一个对象]可能会导致保留周期"在启用ARC的代码中

结果代码是:

_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];
}];
Run Code Online (Sandbox Code Playgroud)

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

Jod*_*ins 14

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

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

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

创建块时,它会捕获其环境,这意味着它将创建对块范围内使用的任何对象的强引用.

考虑到这一点...

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

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

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

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

所以,你想要做的是在栈上创建一个__strong引用,这样只要你想要它,对象就会保持活着状态.

在你的情况下......

[_request setCompletionBlock:^{
    [self setResponseString:_request.responseString];
    [[MyAppDelegate getQueue] addOperation:_operation];
}];
Run Code Online (Sandbox Code Playgroud)

这个块显然有很强的参考价值self.你可能不希望这样.我们试着解决它......

// 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.
    }
}];
Run Code Online (Sandbox Code Playgroud)

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

给它另一个去......

// 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.
    }
}];
Run Code Online (Sandbox Code Playgroud)

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

通过这些更改,您将拥有一个不再具有对self的强引用的块,如果self真的是dealloc,则它会优雅地处理它.

最后,我要重申一下,对strongSelf的赋值是必要的,以防止在检查weakSelf后对象消失的潜在问题.特别...

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.
Run Code Online (Sandbox Code Playgroud)

现在,在这种情况下,块是请求的一部分,这是deallocs很小self的可能性的一部分self,但我的主要观点是使用self来阻止保留周期,但仍然总是通过强引用访问对象 - 弱势的舞蹈.