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)
但事实并非如此。请注意value1
和value2
内容的区别。 value2
缺少冒号。我不明白为什么当 value2
设置为 nil
时 weakValue2
不自归零。一旦我将冒号放回字符串中,我就会得到我期望的结果。 运行 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 类.
我最近从 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)
但事实并非如此。请注意value1
和value2
内容的区别。 value2
缺少冒号。我不明白为什么当 value2
设置为 nil
时 weakValue2
不自归零。一旦我将冒号放回字符串中,我就会得到我期望的结果。 运行 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 类.