为什么iOS中弱NSString属性不会被释放?

5
我编写了下面的示例代码来了解ARC是如何工作的。
@property (nonatomic, weak) NSString *myString;
@property (nonatomic, weak) NSObject *myObj;
@end

@implementation ViewController
@synthesize myString = _myString;
@synthesize myObj = _myObj;
- (void) viewDidAppear:(BOOL)animated
{
    NSLog(@"Appearing Obj: !%@!",self.myObj);
    NSLog(@"Appearing String: !%@!",self.myString);
}

- (void)viewDidLoad
{
    self.myObj = [[NSObject alloc] init];
    self.myString = [[NSString alloc] init];
    NSLog(@"Loading Obj %@",self.myObj);
    NSLog(@"Loading String: !%@!",self.myString);
}

然而,令人惊讶的是,我得到了这些结果:
2012-06-19 15:08:22.516 TESTER[4041:f803] Loading Obj (null)
2012-06-19 15:08:22.517 TESTER[4041:f803] Loading String: !!
2012-06-19 15:08:22.533 TESTER[4041:f803] Appearing Obj: !(null)!
2012-06-19 15:08:22.535 TESTER[4041:f803] Appearing String: !!

如您所见,对象被正确释放了,但我的字符串(也是一个弱属性)没有打印出null……为什么呢?


你所设置在 myString 中的字符串是否是一个字符串字面量(例如 @"Hello world!")? - David Rönnqvist
2个回答

12

NSString 使用许多内部技巧来重用对象、避免不必要的分配和复制。它能够做到这一点,是因为 NSString 实例是不可变的。在这种情况下,可能有一个共享实例来表示一个空字符串,它将通过 [[NSString alloc] init] 返回,并且这个共享实例将被保留在其他地方作为单例。


那似乎有道理。谢谢! - Nosrettap
也许有人可以查看NSString的源代码来确认这一点。 - Mike Weller
虽然这是有道理的,但我认为依赖这种行为可能不明智。这种行为似乎只是某些优化的表现,而且将来可能会发生变化。 - Rob
3
其中一个优化是字符串字面量在应用程序的整个生命周期中都存在。此外,使用对字符串字面量的引用创建一个新字符串([NSString stringWithFormat:@"Hello world"];[[NSString alloc] initWithFormat:@"Hello world"];)会返回对同一字符串字面量的引用(就像我在 我的回答 中提到的那样)。 - David Rönnqvist
我猜当你在字面上调用它时,复制(copy)也会产生相同的共享实例? - Nicolas Miari
是的,在不可变字符串上进行复制只会返回具有+1保留计数的相同实例。但正如Robert所说,您不应该依赖这种行为。 - Mike Weller

6

[[NSString alloc] init] 总是返回相同的值。您可以自行验证。

NSString *string1 = [[NSString alloc] init];
NSString *string2 = [[NSString alloc] init];
NSString *string3 = [[NSString alloc] init];
NSLog(@"string1 = %p, string2 = %p, string3 = %p", string1, string2, string3)

这段代码返回了三个相同的地址。在我的情况下,输出结果如下:

string1 = 0x3e8dd74c, string2 = 0x3e8dd74c, string3 = 0x3e8dd74c

这意味着[[NSString alloc] init]返回的是单例模式。单例通常无法被释放。
使用其他方法创建字符串(例如initWithFormat:)会创建通常可以释放的“非单例”对象,但也有一些例外。
进一步来说,查看源代码(汇编语言):
-[NSPlaceholderString init]:
00040ea4        f64b009c        movw    r0, 0xb89c
00040ea8        f2c00016        movt    r0, 0x16
00040eac            4478        add     r0, pc
00040eae            4770        bx      lr

它会像这样(在Objective-C中):
-(id)init
{
    return SOME_CONSTANT_VALUE;
}

可能是kCFEmptyString,但我不能确定。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接