2

状況:

Objective-Cオブジェクトを不透明な参照(void*)として非同期C APIに渡す必要があります。これは、後で提供するコールバック関数に渡されます。次に、コールバックはオブジェクトのメソッドを呼び出します。コードはガベージコレクションの存在下で機能する必要があり、CAPIの呼び出しとコールバックが呼び出されるまでの間にオブジェクトをGCしてはなりません。

このNSGarbageCollectorクラスは、オブジェクトを収集不可能なルートとしてマークおよびマーク解除するためのメソッドdisableCollectorForPointer:とメソッドを提供します。enableCollectorForPointer:

質問:

ドキュメントやWebの検索で解決できないのは、への呼び出しがカウントされ、同じ数の呼び出しdisableCollectorForPointer:でバランスを取る必要があるかどうかです。enableCollectorForPointer:これが当てはまらず、各オブジェクトにrootのフラグが付けられているかどうかにかかわらず、enableCollectorForPointer:コールバックを呼び出すと、他のコードによって設定された既存のrootフラグがクリアされ、オブジェクトが収集されない場合でも収集される可能性があります。

私が見た代替案は使用することですCFRetain-CFReleaseこれらは私自身のNSObjectサブクラスで安全に使用できますか?以前は、これらの関数を無料でブリッジされたCFオブジェクトでのみ使用していました。

証拠を提供する回答のボーナスポイント。私はdisableCollectorForPointer:、この安全面については言及せずに、至る所で推奨されていると思います。

ノート:

  • このコードはシステム環境設定のPrefPaneによって使用されるため、ガベージコレクションをサポートする必要があります。PrefPaneの64ビットバージョンは、ガベージコレクションを使用する必要があります。したがって、ARCまたは手動の再カウントを使用することはできません。
  • C APIはApple(IOKit)の1つであるため、私もそれを制御できません。
4

1 に答える 1

1

私は答えを見つけたと思います。ガベージコレクタのソースコードは利用できないようですがNSGarbageCollectorNSGarbageCollector.hからのインターフェイスを宣言するヘッダーファイルFoundation.frameworkには次のものが含まれています。

// references outside the heap, globals, and the stack, e.g. unscanned memory, malloc memory, must be tracked by the collector
- (void)disableCollectorForPointer:(void *)ptr;     // this pointer will not be collected...
- (void)enableCollectorForPointer:(void *)ptr;      // ...until this (stacking) call is made

「スタッキング」コメントに注意してください-これは、呼び出しが実際にカウントされることを意味すると思いますか?より多くの証拠がまだ歓迎されています!

アップデート:

念のため、小さなテストプログラム(gcbridgetest.m)を使用して仮説をテストすることにしました。

#import <Foundation/Foundation.h>

@interface PJGarbageCollectionTest : NSObject
@end

@implementation PJGarbageCollectionTest

- (id)init
{
    self = [super init];
    if (!self) return nil;
    NSLog(@"%@ -init", self);
    return self;
}

- (void)finalize
{
    NSLog(@"%@ -finalize", self);
    [super finalize];
}

@end

static void* ext_ptr1 = NULL;
static void* ext_ptr2 = NULL;

static void create()
{
    PJGarbageCollectionTest* test = [[PJGarbageCollectionTest alloc] init];
    [[NSGarbageCollector defaultCollector] disableCollectorForPointer:test];
    ext_ptr1 = test;
    [[NSGarbageCollector defaultCollector] disableCollectorForPointer:test];
    ext_ptr2 = test;
}

static void killref(void** ext_ptr)
{
    [[NSGarbageCollector defaultCollector] enableCollectorForPointer:*ext_ptr];
    *ext_ptr = NULL;
}


int main()
{
    NSLog(@"collector: %@", [NSGarbageCollector defaultCollector]);
    create();
    NSLog(@"Collecting with 2 external references");
    [[NSGarbageCollector defaultCollector] collectExhaustively];
    killref(&ext_ptr1);
    NSLog(@"Collecting with 1 external reference");
    [[NSGarbageCollector defaultCollector] collectExhaustively];
    killref(&ext_ptr2);
    NSLog(@"Collecting with 0 external references");
    [[NSGarbageCollector defaultCollector] collectExhaustively];
    return 0;
}

でコンパイルしgcc -fobjc-gc-only -g -Wall -Wextra -ObjC gcbridgetest.m -framework Foundation -o gcbridgetestて実行すると./gcbridgetest、次の出力が得られ、enable / disableCollectorForPointer:呼び出しが実際にカウントされることを確認します。

2012-06-12 16:08:08.278 gcbridgetest[29712:903] collector: <NSGarbageCollector: 0x20000f420>
2012-06-12 16:08:08.281 gcbridgetest[29712:903] <PJGarbageCollectionTest: 0x20000ee60> -init
2012-06-12 16:08:08.284 gcbridgetest[29712:903] Collecting with 2 external references
2012-06-12 16:08:08.285 gcbridgetest[29712:903] Collecting with 1 external reference
2012-06-12 16:08:08.286 gcbridgetest[29712:903] Collecting with 0 external references
2012-06-12 16:08:08.286 gcbridgetest[29712:903] <PJGarbageCollectionTest: 0x20000ee60> -finalize
于 2012-05-24T14:02:43.103 に答える