10

保持/解放が内部でどのように機能するかについて興味があります。一見すると、の各インスタンスに関連する整数があるように見えます。これは、とを呼び出すと、それぞれNSObject増加および減少します。-retain-release

しかし、を見てみるとNSObject、それが持っている唯一のインスタンス変数は、isaそのクラスタイプを決定するための変数です。

では、個々のオブジェクトの保持カウントはどこに保存されますか?私はそれをいじくり回すつもりではありませんが、私自身の啓蒙のためだけです。

と一緒に保存されていNSObjectますが、Objective Cの実装の詳細に隠されていますか?もしそうなら、それは私には悪いデザインのように思えます。独自のルートクラスを作成し、同様の方法で独自の保持/解放カウントを処理できる必要があります(これは良い考えではありません。使用しない非常に正当な理由が必要NSObjectです)。

4

5 に答える 5

15

保持カウントの保存場所は、使用中のランタイムとクラスの実装の両方によって異なります。

AppleのObjective-Cランタイムの場合、Objective-Cランタイムのソースコードを掘り下げることで多くのことを理解できます。

たとえば、ARCを使用している場合(使用していない場合でも)、ほとんどのオブジェクトの参照カウントはハッシュテーブルに格納されます。_objc_rootRetainの関数を見てくださいruntime/objc-arr.mm。なぜ彼らがこれをしたのか正確にはわかりません。おそらく、これはキャッシュの動作を改善するために保持カウントをまとめて保持する方法です(ARCは非ARCコードよりも頻繁に保持カウントを調整するため、これはARCでは重要です)。

ただし、一部のクラスretainはメソッドと関連メソッドをオーバーライドし、保持カウントを他の場所に格納します。たとえば、メモリリークをデバッグするときに、これを行うことCALayerがわかりました。ランタイムの通常の保持カウントメカニズムを使用する代わりに、CALayerその保持カウントをプライベートC++実装オブジェクトに格納します。これは、Instruments Allocationsインスツルメントがオブジェクトの保持と解放をログに記録しないことを意味するため、かなりイライラしCALayerます。

于 2012-04-11T16:58:43.313 に答える
7

データがどのように保存されるかは正確にはわかりませんが、いくつかのオプションを除外することができます。

プライベート実装変数

このプログラムで示されているように、NSObjectクラスのiVarを反復処理すると、次の1つしか表示されないため、これを除外できます。isa

id object = [NSObject new];
Class meta = object->isa;

printf("class name: %s\n", class_getName(meta));

unsigned count;
Ivar *ivars = class_copyIvarList(meta, &count);

for (int i = 0; i < count; i++) {
    printf("iVar: %s\n", ivar_getName(ivars[i]));
}

free(ivars);

また、プライベート実装プロパティでさえ、クラスmetdataに存在することに注意してください。

プライベートプロパティ

次の例に示すように、プライベートプロパティでさえクラスのメタデータに公開されているため、これを除外することもできます。NSObjectクラスのプロパティはありません。

id object = [NSObject new];    
Class meta = object->isa;

printf("class name: %s\n", class_getName(meta));

objc_property_t *properties = class_copyPropertyList(meta, &count);

for (int i = 0; i < count; i++) {
    printf("property: %s\n", property_getName(properties[i]));
}

関連するオブジェクト

関連するすべてのオブジェクトのリストを取得する直接的な方法がないため、これを除外するのは非常に困難です。ただし、関連付けられたオブジェクトの概念は非常に新しく、参照カウントは永遠に存在しているため、これはあまりありそうにないと思います。

CoreFoundationの構造体マングリング

これが私の一番の推測です。NSObjectを作成すると、それは舞台裏の構造体になります。実際のNSObjectデータ表現は次のようになります。

typedef struct CFObject {
    int retainCount;
    id isa;
} *CFObjectRef;

次に、オブジェクトが作成されると、次のようになります。

id object_createInstance(...)
{
    CFObjectRef object = malloc(sizeof(struct CFObject));

    ...

    return (id) (object + sizeof(object->retainCount));
}

int object_retainCount(id self)
{
    CFObjectRef asObject = (CFObjectRef) (self - sizeof(asObject->retainCount));
    return asObject->retainCount;
}

ただし、これを行う方法は他にもたくさんあるため、これを確認することはできません(たとえば、整数のオブジェクトへのマップ)。

于 2012-04-11T16:44:03.843 に答える
2

そのようには聞こえませんが、念のため...保持カウントを直接使用することを考えている場合は、使用しないでください。

実装の詳細については、WWDC 2011でのセッションで、ARCの下では、参照カウントの実装のほとんどがObjCランタイムに移行したと述べられています。そのためのソースが利用可能であるため、それがどのように機能するかを自分で知ることができるかもしれません。手動の参照カウントの場合、ObjCの動作の多くはCoreFoundationとlibdispatchで複製されます。これらはオープンソースでもあります。同様のスキームを自分で実装しようとしている場合は、それらが教育的であることがわかります。

一般に、これは多くの理由と同じ理由で実装の詳細です。カプセル化は、特にフレームワークプロバイダーにとって適切なポリシーです。実装の詳細に応じてフレームワークのユーザーを望まないのは、コードを壊さずに実装を変更することができないためです。

于 2012-04-11T16:25:40.713 に答える
0

これが適切かどうかはわかりませんが、Objective-Cでの高次メッセージの実装に関するブログ投稿に出くわしました。作成者はHOMオブジェクトをルートクラスとして実装し(つまり、NSObjectから継承されません)、実装は次のようになります。

@interface HigherOrderMessage {
    Class isa;
    NSUInteger retainCount;

    //not relevant to this question part
}

次に、保持カウント管理メソッドは次のように実装されます。

- (id)retain {
    __sync_add_and_fetch(&retainCount, 1);
    return self;
}

- (id)autorelease {
    [NSAutoreleasePool addObject:self];
    return self;
}

- (void)release {
    if (__sync_sub_and_fetch(&retainCount, 1) == 0) {
        [methodSignatureForSelector release];
        [forward release];
        object_dispose(self);
    }
}

このコードは実際に機能するため、retainCountがCocoaクラスにどのように正確に実装されているかはわかりませんが、同様の方法で実装できることは確かです。

于 2012-04-11T17:00:13.917 に答える
0

追加の洞察については、 http://www.mikeash.com/pyblog/friday-qa-2011-09-16-lets-build-reference-counting.htmlをチェックしてください。ここでは、MikeAshがAppleが使用しているような代替実装を検討しています。 。

于 2012-04-11T17:21:01.313 に答える