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