0

例如,如果我们有 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 将第一个版本发送给字符串,将 strongString 分配给 weakString 不会更改保留计数,然后将 nil 分配给 strongString 将第二个版本发送给字符串,甚至将 nil 分配给c2,因此释放 c2 应该将第二个释放发送到字符串,所以现在weakString(以及字符串)的retainCount 应该为零,然后如果我们尝试访问它,那么如果我们尝试访问它,那么weakString 将归零并为零

但是'weakString'仍然包含“bla”所以原始字符串对象,为什么?

4

3 回答 3

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

你会期望两者都是weakStringand weakObjectnil但只有weakObjectis 。这是类中正在进行的一些内部实现优化的结果NSString

于 2013-10-17T22:25:51.987 回答
0
  1. 您计算的保留计数是正确的。

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

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

于 2013-10-18T03:02:39.267 回答
0

释放不会立即发生。

将您的实现放入@autoreleasepool然后打印weakString,它将为零。

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 =(空)

现在,如果您将 weakString 属性更改为 'strong' 而不是 'weak'

@property (strong) NSString *weakString;

输出:str = bla

于 2013-10-18T02:22:01.133 回答