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 に準拠していません]

そうは言っても、できることは、オブジェクトの関連付けを介して 2 つのオブジェクトの有効期間をバインドし、前者の割り当て解除のコールアウトとして後者の割り当て解除を使用することです。

だから、例えば

#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

あなたのコード

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

オブジェクトを自動解放プールに暗黙的に配置します。オブジェクトが割り当てられた後、それを保持したくない (NSArray はオブジェクトを受け取ると保持を行うため) が、すぐに解放することはできません。これが autorelease の目的です。そうしないとオブジェクトが 2 人の所有者の間で途方にくれてしまうような場合に対処するためです。

割り当て時の保持回数は 1 で、その後 autoreleasepool によって保持され、ユーザーによって解放されるため、保持回数は 1 のままです。その後、NSArray によって保持されるため、保持回数は 2 になります。

その後、NSArray が解放され、保持カウントが 1 に戻り、自動解放プールが実行される機会が得られると、オブジェクトは最終的にクリーンアップされます。

NSArray の作成を @autorelease{} 句でラップすることにより、別のプールをネストすることにより、自動解放をより高速に動作させることができます。

于 2013-01-31T00:25:52.980 に答える
2

ARC がこれらの割り当て解除をトリガーする時点

ARC は、静的分析に基づいてコードに割り当て/割り当て解除を挿入します。ソースのアセンブリを見ると、これがどこで行われるかを確認できますProduct -> Generate Output。Xcode に移動します。

参照を削除したときにこれが同期的に発生する可能性があるかどうか

保持/解放/自動解放は常に同期的です。

于 2013-01-31T00:05:12.687 に答える