Objective C - Self Zeroing弱指针意外行为

时间:2015-02-12 05:08:41

标签: objective-c automatic-ref-counting weak-references

我最近从小牛升级到优胜美地,现在我的单元测试失败了。问题归结为字符串内容的弱指针中的拼写错误。请参阅以下示例代码:

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缺少冒号。我不明白为什么weakValue2value2设置为nil时不会自零。一旦我将结肠放回到字符串中,我就得到了我期望的结果。在Mavericks中运行此代码时,我没有看到这种行为。

有谁知道为什么会这样?

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

2 个答案:

答案 0 :(得分:5)

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

如果仅引用它引用的对象,则弱引用将被取消分配。其中的时间实际上并没有得到保证,只要引用是非NULL,所引用的对象仍然可行。

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

NSString,NSNumber,NSDate和一些其他类在某些时候被实现为某些值的单例。或者很多时候,取决于平台。

因此,对这些类之一的实例的弱引用可能会也可能不会被取消,因为实例实际上可能是单例。

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

要解决?

不要创建对系统原语/值类的弱引用。

答案 1 :(得分:3)

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

我发现value1是类__NSCFString的实例,但value2是类NSTaggedPointerString的实例。 NSTaggedPointerString类不支持retain和release(我尝试向它注入一些块方法)。

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

po [value2 retainCount] //18446744073709551615

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

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

之所以发生这种情况,是因为上面的字符串是NSTaggedPointerString类的实例。我认为这是非常奇怪的行为。

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");
    }

我有结果:“相同的字符串”