2

给定以下简单的实现:

@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 在什么时候触发这些释放,以及当我删除引用时这是否会同步发生。

4

4 回答 4

6

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

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

于 2013-01-31T00:04:32.003 回答
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

于 2013-01-31T00:05:23.597 回答
2

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

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

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

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

于 2013-01-31T00:05:12.687 回答
2

你的代码

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 创建。

于 2013-01-31T00:25:52.980 回答