2

私は ARC メモリ管理について学んでいて、意味をなさないものに出くわしました。

以下のサンプル コードでは、main() でローカルに割り当てられたオブジェクトは、予想どおり、ポインターが nil に割り当てられると割り当てが解除されます。

しかし、同じタイプのオブジェクトが別の関数で割り当てられ、そのポインタが main() で定義され、そのポインタが nil に設定されている場合、オブジェクトは main 関数が終了するまで割り当て解除されません。それは私には謎です。

以下のコードでは、クラス GRMemoryChecker の 2 つのインスタンスが作成されます。ある場合には main() で直接割り当てられ、別の場合には main() が itemMakerFunc() を呼び出して割り当てを行います。main() を実行すると、ポインタが nil に設定されている場合、関数で割り当てられたインスタンスの割り当てが解除されていないことがログ出力に示されます。つまり、関数の終了時に割り当てが解除されます。

これは、 itemMakerFunc() で作成されたインスタンスには、ポインタが nil に設定される前に 2 人の所有者がいるのに対し、ローカルに作成されたインスタンスには 1 人しかいないことを意味すると思います。

しかし、なぜ?ポインターが nil に設定された時点でまだ存在している他の所有者は? または、他の所有者がまだ存在しない場合、存在しなくなったときにカウンターが減らされなかったのはなぜですか?

GRMemoryChecker.h:

#import <Foundation/Foundation.h>

@interface GRMemoryChecker : NSObject
{
    NSString *name;
}
- (id)initWithName:(NSString *)str;

- (void)setName:(NSString *)str;

- (void) dealloc;
@end

GRMemoryChecker.m:

#import "GRMemoryChecker.h"

@implementation GRMemoryChecker

- (id)initWithName:(NSString *)str
{
    self = [super init];
    if (self)
    {
        [self setName:str];
    }
    return self;
}

- (void)setName:(NSString *)str
{
    name = str;
}

- (NSString *)description
{
    return name;
}

- (void) dealloc;
{
    NSLog(@"Destroyed: %@", self);
}
@end

main.m:

#import <Foundation/Foundation.h>
#import "GRMemoryChecker.h"

GRMemoryChecker *itemMakerFunc()
{
    return [[GRMemoryChecker alloc] initWithName:@"func-based checker" ];
}

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        GRMemoryChecker *checkerLocallyCreated = [[GRMemoryChecker alloc] initWithName:@"locally-created checker"];
        GRMemoryChecker *checkerFuncBased = itemMakerFunc();

        NSLog(@"Before setting func-based checker pointer to nil");
        checkerFuncBased = nil;
        NSLog(@"After setting func-based checker pointer to nil");

        NSLog(@"Before setting locally-created checker pointer to nil");
        checkerLocallyCreated = nil;
        NSLog(@"After setting locally-created checker pointer to nil");
    }
    return 0;
}

コンソール出力:

Before setting func-based checker pointer to nil
After setting func-based checker pointer to nil
Before setting locally-created checker pointer to nil
Destroyed: locally-created checker
After setting locally-created checker pointer to nil
Destroyed: func-based checker
4

3 に答える 3

5

オブジェクトが関数で作成されたかどうかには依存しませんが、関数に「保持された戻り値」または「保持されていない戻り値」があるかどうかには依存しません ( Objective-C 自動参照カウントを参照)。

デフォルトでは、、、、、およびファミリのメソッドのみが戻り値を保持しています。つまり、関数は (+1) 保持されたオブジェクトを返します。alloccopyinitmutableCopynew

他のすべての関数には、保持されない戻り値があります。少し単純化すると、これらの関数は自動解放された値 (ARC コンパイラによる最適化の対象) を返し、戻り値が呼び出し元の関数で有効であることを保証すると言えます。

現在の自動解放プールが破棄されると、自動解放された値の割り当てが解除されます。

NS_RETURNS_RETAINED次の属性を使用して、関数の動作を変更できます。

GRMemoryChecker *itemMakerFunc() NS_RETURNS_RETAINED;
GRMemoryChecker *itemMakerFunc()
{
    return [[GRMemoryChecker alloc] initWithName:@"func-based checker" ];
}

これで、関数は保持された値を返します。この値は、次のように設定して強い参照を削除するとすぐに割り当てが解除されますcheckerFuncBased = nil

func ベースのチェッカー ポインターを nil に設定する前
Destroyed: func ベースのチェッカー
func ベースのチェッカー ポインターを nil に設定した後
ローカルで作成されたチェッカー ポインターを nil に設定する前
Destroyed: ローカルで作成されたチェッカー
ローカルで作成されたチェッカー ポインターを nil に設定した後
于 2013-05-04T22:17:06.317 に答える
-1

まず、あなたの dealloc メソッドがリークしています。[super dealloc] を呼び出す必要があります。そうしないと、オブジェクトの割り当てが解除されません。

両方のインスタンスの異なる動作については、itemMakerFunc で割り当てられたオブジェクトを返すため、ARC は返すときに「autorelease」を適用します。そのため、そのインスタンスは自動解放されますが、他の保持者が解放してもすぐには解放されません。一方、もう一方のインスタンスは保持され、変数に nil を代入するとすぐに解放されます。自動解放は後付けのアクションだと考えてください。現在のループが終了すると解放されますが、すぐには解放されません。

于 2013-05-04T21:56:50.210 に答える