【问题标题】:Why does using __weak not cause local variable to nil out immediately?为什么使用 __weak 不会导致局部变量立即为零?
【发布时间】:2012-08-30 15:44:50
【问题描述】:

我从事同一个项目已经有一段时间了,随着时间的推移,我对 Objective-C 和 Cocoa 的理解有了一些进步。回顾我的代码的某些部分,我看到了:

__weak ASIFormDataRequest *serverQueueRequest = [ASIFormDataRequest requestWithURL:url2];
[serverQueueRequest setCompletionBlock:^{
    NSLog(@"%@", serverQueueRequest.responseString);
}];
[serverQueueRequest startAsynchronous]; 

这就是我处理所有服务器请求的方式。我认为我这样做是为了抑制警告,即“在块中捕获请求可能会导致保留周期”。所以我把它变弱了,这似乎解决了我所有的问题。我没有注意到这有什么真正的问题。

但是,现在查看代码,它的工作原理似乎有点奇怪。当我将请求声明为__weak 时,它不应该立即归零,因为没有人坚持它吗?为什么这段代码有效?

另外,虽然这段代码有效,但我最近发现了一个不起作用的情况:当连续多次调用包含此代码的方法时,比如在一秒钟内调用 5 次,3/5 请求将有一个NULL 回复。情况一贯如此。删除 __weak 限定符可解决此问题。对此有何解释?

最后,像这样声明本地请求的正确方法是什么?

更新:根据this question,正确的做法是这样的:

ASIHTTPRequest *_request = [[ASIHTTPRequest alloc] initWithURL:...
__weak ASIHTTPRequest *request = _request;

编辑:实际上上面的修复并没有解决调用代码 5 次导致 NULL 响应的问题。那个问题依然存在。解决问题的唯一方法是强烈捕获请求而不使用任何限定符。

现在的问题是为什么我的原始代码仍然有效..

【问题讨论】:

    标签: iphone objective-c ios automatic-ref-counting


    【解决方案1】:

    引用Apple's Programming with ARC 发行说明:

    在堆栈上使用 __weak 变量时要小心。考虑 下面的例子:

    NSString __weak *string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]]; 
    NSLog(@"string:%@", string); 
    

    虽然在初始赋值后使用了字符串, 时没有其他对字符串对象的强引用 任务;因此,它立即被解除分配。日志声明 表明字符串有一个空值。

    【讨论】:

    • 是的,但我要说的是,执行上述操作实际上大部分时间都有效,除非在我非常快速地多次运行代码的特殊情况下。否则,在典型用法下,请求会通过,我会得到响应。
    • ASIHTTPRequest 是否使用 ARC?如果不是,它可能在自动释放池中。在这种特殊情况下,它似乎没有立即发布这一事实并不能证明一个规则。
    • ASIHTTPRequest 不使用 ARC。在执行时不会将它放在块中临时保存它的内存位置吗?
    • 我的观点是,你没有看到弱变量立即变为 nil 的原因是方法返回的对象是自动释放的。啊,是的,好吧,如果你将该调用包装在一个自动释放池中,猜想你可以测试变量是否在池范围之外变为 nil。好主意! [注意:在池范围之外声明变量,在池内调用,在池下记录变量。]
    • 这个变量正是在将应用程序转换为 ARC 后发生在我身上的事情。我用以下代码修复了它:`ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url]; __weak ASIHTTPRequest *weakRequestRef = 请求; [请求 setPostValue:data forKey:@"data"]; // 请求完成 - 通知订阅者 [request setCompletionBlock:^{ [self.delegate requestCompleted:[weakRequestRef responseString]]; }];
    【解决方案2】:

    obj C 堆栈将始终在范围内保留指针。 _weak 并不意味着立即释放,它意味着当堆栈超出范围时释放。

    当你声明一个 var 然后在同一个堆栈范围内对其进行调用时,它不会被释放,直到(最少)堆栈被清理之后。

    块扩展了方法范围,因为它们暗示了潜在的异步行为,并且它们利用了调用它们时存在的堆栈。

    【讨论】:

      【解决方案3】:

      我相信这是因为您在一个块中运行弱变量。该块保持弱变量的状态,从而使其工作。一旦块完成,我打赌在变量上做很多工作可能会导致问题。

      我猜如果你多次运行它会失败,是因为异步 asi 调用堆栈变得很高并且会爆炸。我以前见过这个,如果你非常有耐心,你可以在调试器中捕捉到 asi 爆炸。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-11-01
        • 2014-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-21
        • 2013-04-24
        相关资源
        最近更新 更多