【问题标题】:Hooking end of ARC deallocARC 释放的挂钩端
【发布时间】:2013-01-14 23:36:55
【问题描述】:

给定以下简单的实现:

@implementation RTUDeallocLogger
-(void)dealloc
{
    NSLog(@"deallocated");
}
@end

我们在 ARC 下运行以下代码:

@implementation RTURunner
{
    NSArray* arr;
}
-(void)run{
    arr = [NSArray
           arrayWithObjects:[[RTUDeallocLogger alloc]init],
                            [[RTUDeallocLogger alloc]init],
                            [[RTUDeallocLogger alloc]init],
                            nil];
    NSLog(@"nulling arr");
    arr = NULL;
    NSLog(@"finished nulling");
}
@end

我们得到以下日志输出:

归零arr 完成归零 解除分配 解除分配 解除分配

我想在所有解除分配完成后执行一个操作。这可能吗?

这个问题的目的是真正了解更多关于 ARC 的机制,特别是 ARC 在什么时候触发这些释放,以及当我删除引用时这是否会同步发生。

【问题讨论】:

    标签: ios objective-c automatic-ref-counting dealloc


    【解决方案1】:

    -dealloc 始终是同步的,并且在删除最后一个强引用时发生。但是,对于您的代码, +arrayWithObjects: 可能(如果至少在 -O0 编译)将数组放入自动释放池中,因此当池耗尽时,最后一个强引用被删除,而不是当您将变量设置为 NULL (顺便说一句,你应该对 ObjC 对象使用 nil)。

    您可以通过使用 alloc/init 创建来避免将对象放入自动释放池中,并且您可能(实现细节,bla bla)能够通过打开优化进行编译来避免它.你也可以使用@autoreleasepool { } 来引入一个内部池并以此方式绑定生命周期。

    【讨论】:

    • alloc/init ftw。伟大的。这意味着还有其他一些影响在起作用,我需要提出一个更好的问题来更好地理解我所看到的东西。
    • @spender ... 这个更好的问题的答案是“@autoreleasepool”。
    【解决方案2】:

    ARC 在什么时候触发这些释放

    ARC 根据静态分析将分配/解除分配插入到您的代码中。您可以通过查看源代码的程序集来了解它在哪里执行此操作 - 转到 Xcode 中的 Product -> Generate Output

    当我删除引用时这是否会同步发生

    保留/释放/自动释放始终是同步的。

    【讨论】:

      【解决方案3】:

      如果我是 Apple 的工程师,我可能会争辩说你的问题可能出在你的设计上。您几乎没有理由希望通过观看 dealloc 而不是让 dealloc 自己行动来有效地采取行动。

      [以下是一个巨大的修改:弱属性不通过正常的属性机制,因此它们不符合 KVO,包括最初提议的内部隐式 KVO]

      也就是说,您可以做的是通过对象关联将两个对象的生命周期绑定在一起,并使用后者的 dealloc 作为对前者的 dealloc 的调用。

      所以,例如

      #import <objc/runtime.h>
      
      @interface DeallocNotifier;
      - (id)initWithObject:(id)object target:(id)target action:(SEL)action;
      @end
      
      @implementation DeallocNotifier
      - (id)initWithObject:(id)object target:(id)target action:(SEL)action
      {
          ... blah ...
      
          // we'll use a static int even though we'll never access by this key again
          // to definitely ensure no potential collisions from lazy patterns
          static int anyOldKeyWellNeverUseAgain;
      
          objc_setAssociatedObject(object, &anyOldKeyWellNeverUseAgain, self, OBJC_ASSOCIATION_RETAIN);
      
          ... blah ...
      }
      - (void)dealloc
      {
          [_target performSelector:_action];
      }
      @end
      
      -(void)run{
          arr = ...
      
          [[DeallocNotifier alloc]
                 initWithObject:arr target:self action:@selector(arrayDidDealloc)];
      
          /* you may not even need *arr in this case; I'm unclear as
          to why you have an instance variable for something you don't
          want to keep, so I guess it'll depend on your code */
      } // end of run
      
      
      - (void)arrayDidDealloc
      {
          NSLog(@"array was deallocated");
      }
      

      我假设您能够将您感兴趣的所有对象的生命周期与单个容器的生命周期联系起来;否则您可以将通知器与所有相关对象相关联。

      当您收到arrayDidDealloc 时,该数组肯定已经消失了。

      【讨论】:

      • 当然,这绝对是我设计中的一个缺陷。我有一个不错的后备方法,所以这都是理论上的。真的只是在边缘闲逛。
      • 您可以在对象的dealloc 上附加一个符号断点,这样您就可以逐步了解发生了什么。
      • 如果你试图做的是捎带弱 ivar 来检测对象的释放:那是行不通的。弱指针的自动 nilling 不使用访问器。
      • @NikolaiRuhe 你说的很对; weak 属性不符合 KVO,因此您不能对它们执行松散的内部 KVO。所以我提供了一个完全不同的答案——编辑历史会记录我的错误。
      【解决方案4】:

      你的代码

      arr = [NSArray arrayWithObjects:[[RTUDeallocLogger alloc] init],
                                      [[RTUDeallocLogger alloc] init],
                                      [[RTUDeallocLogger alloc] init],
                                      nil];
      

      将隐式地将对象放入自动释放池中。分配对象后,你不希望它被保留(因为 NSArray 收到对象后会做保留),但你不能立即释放它,否则它永远不会让它活着到 NSArray。这就是自动释放的目的 - 涵盖对象在两个所有者之间处于不确定状态的情况。

      alloc时的retain count是1,然后被autoreleasepool保留,你自己释放,所以retain count还是1。然后,被NSArray保留,retain count变成2。

      稍后,NSArray 被释放,因此保留计数返回 1,当自动释放池有机会运行时,对象最终被清理。

      您可以通过嵌套另一个池来使自动释放动作更快 - 通过使用 @autorelease{} 子句包装您的 NSArray 创建。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-03-12
        • 2013-07-10
        • 1970-01-01
        • 1970-01-01
        • 2013-02-08
        • 2023-03-13
        • 2013-12-17
        相关资源
        最近更新 更多