【问题标题】:ARC: Dealloc releases delegate before I can remove it from observer listARC:在我从观察者列表中删除之前,Dealloc 释放委托
【发布时间】:2025-12-28 02:45:07
【问题描述】:

我有一个非常标准的案例,为了简单起见,假设有 MyController 及其 MyModel。这是 MyModel 的摘录:

@interface MyModel

  @property (nonatomic, weak) id<ModelObserver> *delegate;

@end

@implementation MyModel

- (id)initWithDelegate:(id<ModelObserver>)delegate
{
  self = [super init];
  if (self) {

    _delegate = delegate;

    // Adds observer for model load and changes
    [self addObserver:_delegate];
    ...
}

- (void)dealloc
{
  // Remove delegates and progress indicator
  [self removeObserver:_delegate];

@end

这是 MyController 的摘录

@interface MyController

@property (nonatomic, strong) MyModel *_myModel;

@end

@implementation MyController

- (void)commonInit
{
  _myModel = [[MyModel alloc] initWithDelegate:self];
}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
  self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  if (self) {
    // Custom initialization
    [self commonInit];
  }
  return self;
}

@end

MyModel 有一个类型为 id 的委托(指向 MyController)。 MyController 具有强大的 ivar _myModel。 MyController 创建模型并将自己作为委托传递给 alloc。 MyModel 扩展了 MyBaseModel,并且委托 (MyController) 被添加到观察者列表中并获得有关列表模型更改的通知。问题是,观察者 MyController 在 [self removeObserver:_delegate]; 之前被释放。能够从观察者列表中删除它,所以我有时会崩溃。

已经进行了调试,当执行在“[self removeObserver:_delegate];”上停止时MyController 仍然存在(po _delegate 打印出对象)。但是,在进入 [self removeObserver:_delegate] 之后,_delegate 立即为零,我的意思是 param 从内部为 nil。

更新 如果我在 dealloc 中执行“po _delegate”,我可以看到对象,所以它不是零。但是一旦代码进入 [self removeObserver:_delegate],它的内部就是 nil,所以它不会作为参数传递给“removeObserver:”。但是,当我在调试时从“removeObserver:”返回到 dealloc 时,_delegate 不再为零。

【问题讨论】:

  • 如果_delegate 指向的对象已被释放,但实例变量尚未设置为nil,则会发生您在最后一段中描述的情况,请参阅*.com/questions/16122347/…。跨度>
  • id&lt;ModelObserver&gt; *delegate; 这里应该没有*
  • @MartinR 这是我第一个使用 arc 的项目。在 dealloc 中删除观察者正在手动内存管理中工作。你有什么建议我应该从观察员名单中删除代表吗?
  • 将委托属性声明为assign 而不是weak 可能会解决这个问题。
  • @MartinR,是的,分配(或 unsafe_uretained)的设置成功了。谢谢。 Ps:您可以将您的评论作为答案,以便我接受。

标签: iphone objective-c automatic-ref-counting


【解决方案1】:

这大概是这里发生的事情:

  • MyController实例被释放时,它的实例变量_myModel是 设置为nil,这会导致调用-[MyModel dealloc]
  • 此时,_delegate 实例变量还不是nil,而是正在访问 委托返回nil,因为指向的对象正在处理中 已解除分配。

(有关更详细的分析,请参阅Weak property is set to nil in dealloc but property's ivar is not nil。)

观察弱属性通常是危险的,因为模型无法控制 当这个对象被释放时。

如果您只想拥有与手动引用计数相同的行为,您可以声明 delegate 属性为 assign 而不是 weak。这意味着__unsafe_unretained 实例变量,可以解决您的问题。

【讨论】:

  • 当委托自行释放时,弱委托实例变量变为 nil。使用 _unsafe_unretained,您所拥有的只是一个指向已释放对象的陈旧指针。当然,观察机制也有一个指向已释放对象的陈旧指针,因此删除它_可能有效,但仍然非常危险。