挂钩结束ARC dealloc

时间:2013-01-30 23:55:45

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

鉴于以下简单实现:

@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

我们得到以下日志输出:

nulling arr
finished nulling
deallocated
deallocated
deallocated

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

这个问题的目的是为了更多地了解ARC的机制,特别是ARC在什么时候触发这些解除分配,以及当我丢弃引用时这是否会同步发生。 / em>的

4 个答案:

答案 0 :(得分:6)

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

您可以通过使用alloc / init创建来避免在自动释放池中拥有该对象,并且可能(实现细节,bla bla)能够通过在启用优化的情况下进行编译来避免它。您还可以使用@autoreleasepool {}来引入内部池并以此方式限制生命周期。

答案 1 :(得分:6)

如果我是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时,数组肯定已经消失了。

答案 2 :(得分:2)

  

ARC在什么时候触发这些解除分配

ARC基于静态分析将分配/解除分配插入到代码中。您可以通过查看源代码的汇编来查看它的工作位置 - 转到Xcode中的Product -> Generate Output

  

当我删除引用

时是否可以同步发生这种情况

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

答案 3 :(得分:2)

您的代码

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

将隐式将对象放入自动释放池中。分配对象后,您不希望它保留(因为NSArray会在收到对象后执行保留),但是您无法立即释放它,否则它将永远不会使NSArray活动。这是自动释放的目的 - 用于覆盖两个所有者之间物体处于不稳定状态的情况。

分配时的保留计数为1,然后由自动释放池保留并由您释放,因此保留计数保持为1.然后,它由NSArray保留,因此保留计数变为2.

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

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