【问题标题】:Why isn’t my weak reference cleared right after the strong ones are gone?为什么在强引用消失后我的弱引用没有立即清除?
【发布时间】:2013-02-22 08:48:00
【问题描述】:

我有点固执,但我想很好地理解弱引用和强引用,所以我再次问你。

考虑一下:

__weak NSString* mySecondPointer = myText;   
NSLog(@"myText: %@", myText);

结果是myText: (null),这很明显——弱引用在赋值后被设置为空,因为没有对指向对象的强引用。

但在这种情况下:

__strong NSString* strongPtr = [[NSString alloc] initWithFormat:@"mYTeSTteXt %d"]; 
// weak pointer points to the same object as strongPtr
__weak NSString* weakPtr = strongPtr;
if(strongPtr == weakPtr) 
     NSLog(@"They are pointing to the same obj");        
NSLog(@"StrongPtr: %@", strongPtr);
NSLog(@"weakPtr: %@", weakPtr);

NSLog(@"Setting myText to different obj or nil");

// after line below, there is no strong referecene to the created object:
strongPtr = [[NSString alloc] initWithString:@"abc"];  // or myText=nil;

if(strongPtr == weakPtr) 
     NSLog(@"Are the same");
else
     NSLog(@"Are NOT the same");
NSLog(@"StrongPtr: %@", strongPtr);
// Why weak pointer does not point to nul
NSLog(@"weakPtr: %@", weakPtr);

输出:

2013-03-07 09:20:24.141 XMLTest[20048:207] They are pointing to the same obj
2013-03-07 09:20:24.142 XMLTest[20048:207] StrongPtr: mYTeSTteXt 3
2013-03-07 09:20:24.142 XMLTest[20048:207] weakPtr: mYTeSTteXt 3
2013-03-07 09:20:24.143 XMLTest[20048:207] Setting myText to different obj or nil
2013-03-07 09:20:24.143 XMLTest[20048:207] Are NOT the same
2013-03-07 09:20:24.144 XMLTest[20048:207] StrongPtr: abc
2013-03-07 09:20:24.144 XMLTest[20048:207] weakPtr: mYTeSTteXt 3   // <== ??

我的问题:

为什么strongPtr = [[NSString alloc] initWithString:@"abc"];之后的弱指针值没有变为nil(为什么一开始创建的对象仍然存在于内存中,尽管它没有任何强引用?——或者它可能有?)


我试过那个:(但我想添加评论并不好)。我在@autorealesepool 中包含了创建strongPtr 的代码。我不确定它是否是正确的解决方案,但它可以工作......

 __strong NSString* strongPtr;
    __weak NSString* weakPtr;
    @autoreleasepool {


        strongPtr = [[NSString alloc] initWithFormat:@"mYTeSTteXt %d", 3];

        // weak pointer point to object create above (there is still strong ref to this obj)
        weakPtr = strongPtr;
        if(strongPtr == weakPtr) NSLog(@"They are pointing to the same obj");        

        NSLog(@"StrongPtr: %@", strongPtr);
        NSLog(@"weakPtr: %@", weakPtr);

        NSLog(@"Setting myText to different obj or nil");   

    // after line below, there is no strong referecene to the created object:
     strongPtr = [[NSString alloc] initWithString:@"abc"];  


    }

    if(strongPtr == weakPtr) 
        NSLog(@"Are the same");
    else
        NSLog(@"Are NOT the same");
    NSLog(@"StrongPtr: %@", strongPtr);
    // Why weak pointer does not point to nul
    NSLog(@"weakPtr: %@", weakPtr);

输出:

2013-03-07 09:58:14.601 XMLTest[20237:207] They are pointing to the same obj
2013-03-07 09:58:14.605 XMLTest[20237:207] StrongPtr: mYTeSTteXt 3
2013-03-07 09:58:14.605 XMLTest[20237:207] weakPtr: mYTeSTteXt 3
2013-03-07 09:58:14.606 XMLTest[20237:207] Setting myText to different obj or nil
2013-03-07 09:58:14.607 XMLTest[20237:207] Are NOT the same
2013-03-07 09:58:14.607 XMLTest[20237:207] StrongPtr: abc
2013-03-07 09:58:14.608 XMLTest[20237:207] weakPtr: (null)

【问题讨论】:

  • +1 很好的问题示例!
  • @FruityGeek,你能解释一下这个问题,而不是仅仅放弃聪明的言论吗?
  • @FruityGeek:还有其他提示吗?
  • if(strongPtr == weakPtr)NSLog(@"weakPtr: %@", weakPtr); 似乎都创建了临时/自动发布的引用。如果您删除这些行,weakPtr 在末尾就像预期的那样是nil

标签: objective-c weak-references reference-counting strong-references


【解决方案1】:

从汇编代码可以看出,访问weakPtr会产生一个objc_loadWeak调用。

根据Clang documentationobjc_loadWeak保留并自动释放对象,相当于

id objc_loadWeak(id *object) {
  return objc_autorelease(objc_loadWeakRetained(object));
}

这(希望)解释了为什么两者都

if(strongPtr == weakPtr) ...

NSLog(@"weakPtr: %@", weakPtr);

创建额外的自动发布引用。

这不是一个特殊的NSString 问题,我可以使用自定义(普通)类重现相同的行为。

【讨论】:

  • 听起来有点高级,但很合理;)谢谢。
  • 不错的收获!我在大会上眯了一会儿眼睛,但没有去查看objc_loadWeak 电话。这真的意味着每个弱引用访问都要经过两次额外的内存管理调用吗?
  • @zoul:谢谢! - 是的,即使在优化(发布)代码中我也找到了调用。原因可能是避免在多线程环境中访问弱引用时被取消,但这纯粹是猜测。 - 顺便提一句。我通过使用覆盖保留/释放/自动释放的自定义非 ARC 类来解决这个问题。 (我永远不会承认我首先在调试器中检查了 retainCount,因为这会导致立即被否决:-)
  • 知道弱引用不能在你的代码块中间消失是一个很好的保证,否则一个普通的if (weakRef != nil) {…} 将毫无意义。 (Similar thing 由 ARC 保证用于方法参数。)至于 retainCount 失效,我现在不能对你投反对票,但半小时的自我鞭笞就可以了。
  • 感谢您的发现...令人沮丧的是:developer.apple.com/library/mac/#releasenotes/ObjectiveC/… 在堆栈上使用 __weak 变量时要小心。考虑以下示例: NSString * __weak string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]]; NSLog(@"string: %@", string);日志语句显示该字符串具有空值。 (编译器会在这种情况下提供警告。)
【解决方案2】:

首先,不要在NSString 上尝试弱引用或其他内存管理行为,那个类有太多的魔力。并不是说弱引用不适用于NSString,只是行为比您预期的要复杂一些,并且很容易导致错误的结论。请参阅这些以前的问题:

当您使用自动释放池包装代码示例并随后记录弱字符串指针时,它确实是 nil。甚至可能会出现与NSString 以外的类类似的行为——您根本无法保证在您失去对对象的最后一个强引用的那一刻,弱引用将被清除。或者也许你是,但很难说最后一个强引用到底什么时候消失了,因为自动释放池正在发挥作用,正如这个例子所暗示的那样(马丁的回答很好地解释了)。

【讨论】:

  • 是的,谢谢...
  • 这不是NSString的特刊。我用自定义类重现了相同的行为。访问弱指针会创建自动释放的引用,请参阅我的答案。
  • @guitar_freak:最好保留这个问题,因为 Martin 的回答准确地解释了这种行为。你能把它标记为接受的吗?
【解决方案3】:

当你这样做时

strongPtr = [[NSString alloc] initWithString:@"abc"]

你的 strongPtr 指向新分配的对象,由于它指向的前一个对象也没有被释放,弱指针仍然指向一个有效的地址。

顺便说一句。您可以使用

打印对象的内存地址

NSLog(@"%@", [NSString stringWithFormat:@"%p", theObject])

【讨论】:

  • 是的,但是如果我写strongPtr = nil,结果是一样的。
  • 假设 ARC,前一个实例不可能泄漏,应该被释放。最终。
  • strongPtr = nil 与向对象发送 dealloc 不同。
  • @peko,但假设没有其他强引用,它应该将任何弱引用归零
  • 我相信问题出在 is 仍然保留一个引用...一个自动释放的引用,一旦您退出当前的自动释放池,它将被清理。试试吧。将上述内容包装在 AutoRelease 池中,然后检查该池之外的值。可以肯定的是,它在那个时候会像预期的那样为空。另外,请参阅我的示例以了解有关此问题的另一种观点。
【解决方案4】:

不确定 OP 的问题和/或此处接受的答案是否仍然有效,至少与我在 iOS9/Xcode7 中看到的结果不同。

这是 OP 代码的(稍微清理过的)版本...

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[])
{
    @autoreleasepool
    {
        NSString* __strong strongPtr = [[NSString alloc] initWithFormat:@"Life, Universe, Everything: %d", 42];
        NSString* __weak   weakPtr   = strongPtr;

        NSLog(strongPtr == weakPtr ? @"Same" : @"Different");
        NSLog(@"  StrongPtr: %@", strongPtr);
        NSLog(@"  weakPtr:   %@", weakPtr);

        NSLog(@"Changing strongPtr to something else...");
        // After this is set, there is no strong reference to the created object
        strongPtr = [[NSString alloc] initWithFormat:@"Drink: %@", @"Pan-galactic Gargle Blaster!"];

        NSLog(strongPtr == weakPtr ? @"Same" : @"Different");
        NSLog(@"  StrongPtr: %@", strongPtr);
        NSLog(@"  weakPtr:   %@", weakPtr);
    }

    return 0;
}

这是(截断的)输出...

Same
  StrongPtr: Life, Universe, Everything: 42
  weakPtr:   Life, Universe, Everything: 42

Changing strongPtr to something else...

Different
  StrongPtr: Drink: Pan-galactic Gargle Blaster!
  weakPtr:   (null)

Program ended with exit code: 0

在这里访问条件句中的弱引用(根据接受的答案的解释)不会保留自动释放的引用,正如您在输出中的 (null) 所看到的那样。

...或者我是否不小心将 OP 的问题更改为我隐藏了他所看到的内容?或者可能是因为现在 ARC 默认开启?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-03
    • 2014-06-17
    • 1970-01-01
    • 2013-07-06
    • 1970-01-01
    • 1970-01-01
    • 2012-11-05
    相关资源
    最近更新 更多