当释放对象时,使用ARC分配对弱引用的强引用?

时间:2013-10-17 22:09:26

标签: ios objective-c properties automatic-ref-counting

例如,如果我们有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”所以原来的字符串对象,为什么?

3 个答案:

答案 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);

您希望weakStringweakObject都是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)

  1. 您计算的保留计数是正确的。

  2. ARC只是在编译时为您添加保留/释放代码,因此基本规则与手动管理内存相同。当保留计数为零时,它将立即被解除分配。

  3. 上面的示例是一个特例:NSString位于为性能而管理的特殊内存下。字符串内容是不可变的。相同的字符串内容将指向相同的内存地址,不会重复多次。 NSString保留计数太大而无法释放。