NSNumber和其他人在发布时未解除分配

时间:2018-04-08 22:29:08

标签: objective-c clang automatic-ref-counting foundation address-sanitizer

我正在尝试了解有关手动引用计数的更多信息。 我创建了一个带有实例变量的类及其使用手动版本的getter / setter:

@interface Foo: NSObject {
    id bar;
}
- (id)bar;
- (void)setBar:(id)value;
@end

@implementation Foo
- (id)bar {
    return bar;
}

- (void)setBar:(NSNumber*)value {
    [bar release];
    bar = value;
}
@end

然后我就这样使用它:

int main() {

    Foo *f = [[Foo alloc] init];
    NSNumber *n = [[NSNumber alloc] initWithInteger: 3];

    [f setBar: n];
    [n release];

    NSLog(@"%@\n", [[f bar] description]);

    return 0;
}

我使用Address Sanitizer编译它并且我期望它陷阱,因为[[f bar] description]应该在解除分配的对象上调用方法:

clang -fno-objc-arc -fsanitize=address -g -framework Foundation main.m
ASAN_OPTIONS=detect_leaks=1

但令我惊讶的是它运作良好!

2018-04-08 18:18:38.470300-0400 a.out[3457:291626] 3

然后我用NSDate尝试了它,看它是否有所作为:

NSDate *n = [[NSDate alloc] init];

[c setBar: n];
[n release];

确实如此:

ASAN:DEADLYSIGNAL
=================================================================
==3379==ERROR: AddressSanitizer: SEGV on unknown address 0x7fff0b800018 (pc 0x7fff577f3e9d bp 0x7ffee4491670 sp 0x7ffee44915f8 T0)
==3379==The signal is caused by a READ memory access.
    #0 0x7fff577f3e9c in objc_msgSend (libobjc.A.dylib:x86_64h+0x6e9c)
    #1 0x7fff5841b014 in start (libdyld.dylib:x86_64+0x1014)
...

我调整NSNumber的{​​{1}}方法,发现它没有被调用:

dealloc

@interface Dealloc: NSObject @end @implementation Dealloc - (void)dealloc { NSLog(@"dealloc"); } @end ... method_exchangeImplementations(class_getInstanceMethod([Dealloc class], @selector(dealloc)), class_getInstanceMethod([NSNumber class], @selector(dealloc))); 2018-04-08 18:18:38.470300-0400 a.out[3457:291626] 3 相比:

NSDate

所以我接着尝试了一堆其他类:

  • 2018-04-08 18:21:03.899490-0400 a.out[3483:293512] dealloc 2018-04-08 18:21:03.900538-0400 a.out[3483:293512] dealloc 2018-04-08 18:21:03.900598-0400 a.out[3483:293512] dealloc 取消分配
  • NSString 获得分配
  • NSMeasurement 取消分配
  • NSArray 取消分配
  • NSDictionary 取消分配
  • NSData 取消分配

这里发生了什么?为什么只有其中一些被Address Sanitizer接收

更新

正如Rob和rmaddy指出的那样,对于NSURL的小整数值,它被存储为标记指针,实际上并没有分配任何额外的内存:

NSNumber

最后一位设置为1,表示它已被标记,前4位的所有内容都是整数42。

对于带(lldb) p/t n (__NSCFNumber *) $1 = 0b0000000000000000000000000000000000000000000000000010101000100111 (int)42 的常量字符串(__NSCFConstantString),它似乎不是标记指针,因为最后一位未设置,但它仍然是一个奇怪的地址:

[[NSString alloc] initWithString:@"blah"]

(lldb) p n (__NSCFConstantString *) $1 = 0x0000000100002068 @"blah" (lldb) p/t n (__NSCFConstantString *) $2 = 0b0000000000000000000000000000000100000000000000000010000001101000 @"blah" NSDate都显示在“正常”地址范围内> 0x600000000000。

NSMeasurement的地址在此范围内,但不会解除分配,但填充对象会:

NSArray

1 个答案:

答案 0 :(得分:2)

如果您想学习手动保留版本,有两个重要细节:

  1. 请不要尝试使用任何框架类来保存TextBox lastBox = BoxGrid.Children.OfType<TextBox>().Last(); ResultBox.Text = lastBox.Text; 。子类NSObject并在所述子类上进行实验。框架对象通常会有各种奇怪的实现细节...... 令人惊讶
  2. 请勿使用NSObjectretainCount。由此产生的价值在现实世界中毫无意义。将保留计数视为增量;你导致它增加,每增加一次,你必须使它减少。
  3. 另外,浏览Xcode调试器中的内存窗格。