我不理解弱引用和强引用

3

首先,使用ARC(自动引用计数)解释了“无效”引用的问题,就像这个例子:

NSDate* date1=[[NSDate alloc]init];
NSDate* date2=date1;
[date1 release];
NSLog(@"%@",date2); // bad access

所以我已经理解了保留/释放机制:在这种情况下,指令将是:
date2=[date1 retain];

但当涉及到强引用/弱引用时,对我来说听起来像个矛盾:
“默认情况下,引用是强引用。如果您将对象分配给一个强引用,ARC会认为您希望该对象保留并隐式地保留它”
这不是与之前所说的相矛盾吗?date2默认是强引用,因此它应该隐式地保留date1,不应该出现访问异常。
当然,我可能误解了一些内容,有人可以更好地向我解释吗?
2个回答

15

强引用是默认的,因为通常这是你想要的,但是使用ARC,编译器会分析对象的生命周期需要多长时间,并在适当的时候释放内存。例如:

- (void)someMethod
{
  NSDate* date = [[NSDate alloc] init]; // date is __strong by default
  NSLog(@"The date: %@", date); // date still contains the object created above

  // Sometime before this point, the object date pointed to is released by the compiler
}

弱引用只在对象有一个或多个其他强引用时保留该对象。一旦最后一个强引用被断开,编译器将释放对象,并且运行时会将弱对象引用(变量)更改为 nil 。这使得像上面的示例中那样局部范围内的弱变量几乎无用。例如:

- (void)someMethod
{
  __weak NSDate* date = [[NSDate alloc] init]; // The date created is released before it's ever assigned to date 
                                               // because date is __weak and the newly created date has no 
                                               // other __strong references
  NSLog(@"The date: %@", date); // This always prints (null) since date is __weak
}

为了看到在本地作用域中弱变量和强变量协同工作的示例(这仅具有极为有限的实用性,并且仅在此处显示以演示弱变量引用):

- (void)someMethod
{
  NSDate* date = [[NSDate alloc] init]; // date stays around because it's __strong
  __weak NSDate* weakDate = date;

  // Here, the dates will be the same, the second pointer (the object) will be the same
  // and will remain retained, and the first pointer (the object reference) will be different
  NSLog(@"Date(%p/%p): %@", &date, date, date);
  NSLog(@"Weak Date(%p/%p): %@", &weakDate, weakDate, weakDate);

  // This breaks the strong link to the created object and the compiler will now
  // free the memory. This will also make the runtime zero-out the weak variable
  date = nil;

  NSLog(@"Date: %@", date); // prints (null) as expected
  NSLog(@"Weak Date: %@", weakDate); // also prints (null) since it was weak and there were no more strong references to the original object
}

只是一个问题,在第二个示例代码中,当您说:“由于date是__weak类型,它总是打印(null)”,date是nil,但是否存在内存泄漏?或者已经被释放了? - Ramy Al Zuhouri
@RamyAlZuhouri 没有内存泄漏。编译器立即释放新分配的日期,甚至不会费心进行赋值(在这种情况下)。 - Jason Coco
NSLog(@"Weak Date: %@", date); 这不应该是 NSLog(@"Weak Date: %@", weakDate); 吗? - NMunro

3
关键错误在于将手动保留释放行为与ARC视为相同。但实际上它们是不同的。在ARC下,对象赋值既是原子操作,又是决定对象生命周期的表达式。
以你提供的例子为例,去掉 retain 后,代码如下:
NSDate* date1=[[NSDate alloc]init];
NSDate* date2=date1;
NSLog(@"%@",date2);

在ARC下完全合理; 没有手动释放会弄乱事情。也就是说,它自动工作。

更好的是,因为编译器在幕后执行流控制分析,所以不需要额外的保留或释放。代码将字面上是这样的:

NSDate* date1; // date1 initialized to nil -- or not given that it is never read before...
date1 = [[NSDate alloc]init]; // ... this assignment (of a +1 retain count obj)
NSDate* date2=date1; // retain count unchanged
NSLog(@"%@",date2); // retain count unchanged
.... compiler emits equivalent to [date1 release] ...

因为编译器在date1 或者 date2 的最后一次使用之后才会生成release,所以永远不会出现悬空指针。


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