关于ios:当对象被释放时,使用ARC分配对弱引用的强引用?

When the object will be released assigning a strong reference to a weak reference, using ARC?

例如,如果我们有两个具有nsstring属性的对象,一个是弱的,另一个是强的,就像这样

1
2
3
4
5
6
7
8
@interface Class1 : NSObject
@property (weak) NSString *weakString;
@end


@interface Class2 : NSObject
@property (strong) NSString *strongString;
@end

然后这样做:

1
2
3
4
5
6
7
8
9
10
11
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;

甚至

1
c2 = nil;

那么,c1.weakstring包含什么呢?

将字符串赋给strongstring调用保留字符串,将字符串赋给nil发送第一个释放给字符串,将strongstring赋给weakstring不更改保留计数,然后将nil赋给strongstring发送第二个释放给字符串,甚至将nil赋给c2,因此释放c2应将第二个释放发送给字符串,并且所以现在weakString(和so-string)的retaincount应该是零,然后释放,如果我们试图访问它,weakString将为零

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


NSString是一个类簇,在后台进行一些非结构化的优化。如果您用一些自定义的NSObject子类而不是NSString子类重复测试,它的行为将更像您所期望的那样。

想象一下您的例子中的以下变化:

1
2
3
4
5
6
7
8
9
10
11
12
@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

然后考虑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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类中进行的一些内部实现优化的结果。


  • 您计算的保留计数是正确的。

  • ARC只是在编译时为您添加保留/释放代码,因此基本规则与手动管理内存相同。当retain count为零时,将立即解除锁定。

  • 上面的示例是一个特殊的情况:nsstring处于为性能而管理的特殊内存下。字符串内容是不可变的。相同的字符串内容将指向相同的内存地址,以避免多次重复。nsstring保留计数太大,无法释放。


  • 释放不会立即发生。

    将您的实现放在@autoreleasepool中,然后打印WeakString,它将为零。

    (使用initWithFormat进行字符串初始化,而不是使用冗余的initWithString)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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属性改为"强"而不是"弱"

    1
    @property (strong) NSString *weakString;

    输出:str=bla