为什么@property使用weak和__weak修饰符的实例变量行为不同?

6

我知道strongweak是在属性声明中使用的修饰符,而__strong__weak是在实例变量声明中使用的...strong表示只要我拥有该对象就将其保存在内存中,而weak表示只要其他人具有对它的强引用,就将其保存在内存中...对吗?但我不明白为什么在属性中使用weak,而在实例变量中使用__weak时行为不同?这就是我的疑惑所在...

 @interface DemoViewController (){

    __weak NSArray *weakArray;
    __strong NSArray *strongArray;
    __weak NSString *weakString;
    __strong NSString *strongString;  
 }

@property (weak) NSString *weakStringProperty;
@property (strong) NSString *strongStringProperty;

@property (weak) NSArray *weakArrayProperty;
@property (strong) NSArray *strongArrayProperty;

@end

@implementation DemoViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    strongArray = [[NSArray alloc] initWithObjects:@"one",@"two", nil];
    weakArray = strongArray;

    NSLog(@"Round:1 strongArray is %@.", strongArray);
    NSLog(@"Round:1 weakArray is %@.", weakArray);

    strongArray = nil;

    NSLog(@"Round:2 strongArray is %@.", strongArray);
    NSLog(@"Round:2 weakArray is %@.", weakArray);

    self.strongArrayProperty = [[NSArray alloc] initWithObjects:@"one",@"two", nil];
    self.weakArrayProperty = self.strongArrayProperty;

    NSLog(@"Round:1 strongArrayProperty is %@.", self.strongArrayProperty);
    NSLog(@"Round:1 weakArrayProperty is %@.", self.weakArrayProperty);

    self.strongArrayProperty = nil;

    NSLog(@"Round:2 strongArrayProperty is %@.", self.strongArrayProperty);
    NSLog(@"Round:2 weakArrayProperty is %@.", self.weakArrayProperty);


    strongString = [[NSString alloc]initWithFormat:@"instanceVariable"];
    weakString = strongString;

    NSLog(@"Round:1 strongString is %@.", strongString);
    NSLog(@"Round:1 weakString is %@.", weakString);

    strongString = nil;

    NSLog(@"Round:2 strongString is %@.", strongString);
    NSLog(@"Round:2 weakString is %@.", weakString);

    self.strongStringProperty = [[NSString alloc]initWithFormat:@"Property"];
    self.weakStringProperty = self.strongStringProperty;

    NSLog(@"Round:1 strongStringProperty is %@.", self.strongStringProperty);
    NSLog(@"Round:1 weakStringProperty is %@.", self.weakStringProperty);

    self.strongStringProperty = nil;

    NSLog(@"Round:2 strongStringProperty is %@.", self.strongStringProperty);
    NSLog(@"Round:2 weakStringProperty is %@.", self.weakStringProperty);

}
@end

以下是生成的日志:
Round:1 strongArray is (
    one,
    two
).
 Round:1 weakArray is (
    one,
    two
).
 Round:2 strongArray is (null).
 Round:2 weakArray is (null).


 Round:1 strongArrayProperty is (
    one,
    two
).
Round:1 weakArrayProperty is (
    one,
    two
).
Round:2 strongArrayProperty is (null).
Round:2 weakArrayProperty is (
    one,
    two
).           —???

Round:1 strongString is instanceVariable.
Round:1 weakString is instanceVariable.
Round:2 strongString is (null).
Round:2 weakString is (null).


Round:1 strongStringProperty is Property.
Round:1 weakStringProperty is Property.
Round:2 strongStringProperty is (null).
Round:2 weakStringProperty is Property.   ——??

当弱引用对象被设置为nil后,两个弱实例变量都会打印(null),这是符合预期的。但我想知道为什么弱属性weakStringProperty和weakArrayProperty仍然打印它们之前的值,并且表现得像它们强指向strongStringProperty和strongArrayProperty一样?

谢谢 :)


我能够在第二轮中获取 strongArrayProperty 和 weakArrayProperty 的值为 null。不过,weakStringProperty 在第二轮中仍然是 "Property"。这非常奇怪,因为 ARC 理论上应该处理好这个。附注:我在自己的电脑上运行了这个程序。 - Kunal Shrivastava
我认为原因是弱引用属性被保留了,因为主方法有一个包含应用程序运行的自动释放池。由于自动释放池的存在,弱引用属性不会立即设置为nil。尽管我尝试在viewDidAppear和didReceiveMemoryWarning中访问它(通过模拟内存警告),但它仍然没有变成nil。 - Kunal Shrivastava
奇怪!!当我在实际设备上运行这段代码时,弱实例变量和弱属性都给出了预期的结果...这意味着问题只存在于模拟器中。无论如何,感谢@Kunal Shrivastava - Dipika
2个回答

1
你的属性是原子性的,因为你没有声明它们为非原子性。原子性属性返回一个被保留和自动释放的对象,所以这个对象会一直留在自动释放池中,并且会一直被保留,直到你退出viewDidLoad方法。
例如,进行更改:
@property (weak, nonatomic, readwrite) NSString *weakStringProperty; 

如果你按照预期的方式操作,你更有可能得到预期的结果。或者稍后在另一个方法中检查属性,弱引用属性很可能为nil。

然而,在iOS中经常创建永远不会释放的对象。例如,有一个空的NSArray对象永远不会被释放。同样的情况也适用于许多NSNumber对象、短字符串、@YES和@NO等其他对象。不能保证对象将在你认为它应该被释放时被释放,因此无法保证弱对象变为nil。


在模拟器上...我尝试了你说要做的更改,结果是...在viewDidLoad和viewWillAppear中,属性仍然没有被设置为nil,但在viewDidAppear中被设置为nil。但奇怪的是,当我在设备上运行时,所有弱引用属性都会在它们所指向的对象被设置为nil后被设置为nil,并记录了预期的结果。无论如何,谢谢 :) - Dipika

1

weakStringProperty不是nil,因为Foundation仍在保留它。

当您调用[[NSString alloc]initWithFormat:@"Property"]时,Foundation决定将字符串作为NSTaggedPointerString进行内部化,然后保留以供将来使用。您可以通过记录类来验证此操作:

NSLog(@"kindof: %@", [self.weakStringProperty class]);

我不确定系统用于决定创建这些字符串的所有标准,但长度和可变性是因素。以下任何一个都可以得到您想要的结果。

// Longer string
self.strongStringProperty = [[NSString alloc]initWithFormat:@"Property la la la"];

// Mutable string
self.strongStringProperty = [[NSMutableString alloc]initWithFormat:@"Property"];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接