Objective C - 自归零弱指针意外行为

Objective C - Self Zeroing weak pointer unexpected behaviour

我最近从 Mavericks 升级到 Yosemite,现在我的单元测试失败了。问题归结为指向字符串内容的弱指针中的拼写错误。请看下面的示例代码:

NSString* value1;
NSString* value2;
__weak NSString* weakValue1;
__weak NSString* weakValue2;
NSMutableString* resultText = [NSMutableString new];

@autoreleasepool
{
    value1 = [NSString stringWithFormat: @"Hello: %d", 1];
    value2 = [NSString stringWithFormat: @"Hello %d", 2];
    weakValue1 = value1;
    weakValue2 = value2;

    [resultText appendFormat: @"  value1 = %@", weakValue1];
    [resultText appendFormat: @"  value2 = %@", weakValue2];

    value1 = nil;
    value2 = nil;
}
[resultText appendFormat: @"  value1 = %@", weakValue1];
[resultText appendFormat: @"  value2 = %@", weakValue2];

NSLog( @"resultText = %@", resultText );

这段代码的输出是:

resultText =   value1 = Hello: 1  value2 = Hello 2  value1 = (null)  value2 = Hello 2

我本以为:

resultText =   value1 = Hello: 1  value2 = Hello 2  value1 = (null)  value2 = (null)

第二个 value2 也是 (null) 但事实并非如此。请注意value1value2内容的区别。 value2 缺少冒号。我不明白为什么当 value2 设置为 nilweakValue2 不自归零。一旦我将冒号放回字符串中,我就会得到我期望的结果。 运行 Mavericks 中的这段代码时,我没有看到这种行为。

有人知道为什么会这样吗?

我在这个 question 中注意到,多线程的有效点可能会导致弱指针及时不自零,但这不是这里发生的事情。首先,我不是多线程的,如果我单步执行代码并查看变量而不是使用 NSLog 打印它们,我会得到完全相同的结果。

NSString 它不是具体的 class,它是许多其他 classes 的接口和工厂。

我发现 value1 是 class __NSCFString 的实例,但 value2 是 class NSTaggedPointerString 的实例。 Class NSTaggedPointerString 不支持保留和释放(我尝试向它的方法中注入一些块)。

如果您为此实例打印 retainCount,您将得到该结果:

po [value2 retainCount] //18446744073709551615

如果从格式中删除符号“:”,您将得到以下结果:

 resultText =   value1 = Hello 1  value2 = Hello 2  value1 = Hello 1  value2 = Hello 2

这是因为上面的字符串是 NSTaggedPointerString class 的实例。我认为这是非常奇怪的行为。

P.S。

如果您将“:”添加到 value2 字符串,您将得到结果:

resultText =   value1 = Hello: 1  value2 = Hello: 2  value1 = (null)  value2 = (null)

:)

UPD

如果字符串长度小于8,那么它会被缓存到内存中。

UPD-2

我已将代码更改为:

value1 = [NSString stringWithFormat: @"Heo: %d", 1];
    value2 = [NSString stringWithFormat: @"Heo: %d", 1];
    if (value2 == value1)
    {
        NSLog(@"same strings");
    }

我得到了结果:"same strings"

问题是您对弱引用的使用与系统的实现细节发生冲突。

当且仅当它引用的对象被释放时,弱引用才会被取消。其中的时序实际上并不能保证,只要引用非NULL,引用的对象仍然可行。

对单例(永远不会释放的真正单例)的弱引用永远不会被取消。

NSString、NSNumber、NSDate 和少数其他 类 有时会作为某些值的单例实现。或者很多时候,具体取决于平台。

因此,对其中一个实例的弱引用 类 可能会也可能不会被取消,因为该实例实际上可能是一个单例。

请注意,根据定义,标记指针实际上是单例。

要修复?

不要创建对系统的弱引用 primitive/value 类.