例如,如果我们有2个具有NSString属性的对象,一个是弱的,一个是这样的强
@interface Class1 : NSObject
@property (weak) NSString *weakString;
@end
@interface Class2 : NSObject
@property (strong) NSString *strongString;
@end
然后这样做:
NSString *string = [[NSString alloc] initWithString:@"bla"];
Class2 *c2 = [[Class2 alloc] init];
c2.strongString = string;
string = nil;
Class1 *c1 = [[Class1 alloc] init];
c1.weakString = c2.strongString;
c2.strongString = nil;
甚至
c2 = nil;
然后c1.weakString包含什么?
将字符串赋值给strongString调用一个保留字符串,将字符串赋值给nil将第一个版本发送给字符串,将weakString赋值给weakString不会改变保留计数,然后将nil分配给strongString将第二个版本发送到字符串甚至分配nil到c2,所以释放c2应该将第二个版本发送到字符串,所以现在,weakString的retainCount(以及字符串)应该为零然后释放,因此如果我们尝试访问它,则weakString归零为nil
但'weakString'仍然包含“bla”所以原来的字符串对象,为什么?
3 回答
解除分配不会立即发生 .
将您的实现放在
@autoreleasepool
然后打印weakString,它将是零 .(使用
initWithFormat
进行字符串初始化而不是冗余initWithString
)输出: str = (null)
现在,如果将weakString属性更改为“strong”而不是“weak”
输出: str = bla
您计算的保留计数是对的 .
ARC只是在编译时为您添加保留/释放代码,因此基本规则与手动管理内存相同 . 当retain count为零时,它将立即被释放 .
上面的例子是一个特例:NSString在为性能管理的特殊内存下 . 字符串内容是不可变的 . 相同的字符串内容将指向相同的内存地址,不会重复多次 . NSString保留计数太大而无法释放 .
NSString
是一个类集群,并在后台进行一些不直观的优化 . 如果您使用某些自定义NSObject
子类而不是NSString
重复测试,它将表现得更像您期望的那样 .想象一下您的示例的以下变化:
然后考虑:
你希望
weakString
和weakObject
都是nil
,但只有weakObject
. 这是NSString
类中正在进行的一些内部实现优化的结果 .