2

情况:

我需要将 Objective-C 对象作为不透明引用 ( void*) 传递给异步 C API,稍后将传递给我提供的回调函数。回调然后调用对象上的方法。代码需要在存在垃圾收集的情况下工作,并且在我对 C API 的调用和我的回调被它调用的时间之间不得对对象进行 GC。

该类NSGarbageCollector提供disableCollectorForPointer:enableCollectorForPointer:方法用于将对象标记和取消标记为不可收集的根。

问题:

我无法从文档和网络搜索中解决的问题是,disableCollectorForPointer:是否计算调用次数并且必须通过相同数量的enableCollectorForPointer:调用来平衡。如果不是这种情况,并且每个对象都被标记为 root,那么enableCollectorForPointer:在我的回调中调用可能会清除由其他代码设置的现有 root 标志,从而导致对象被收集,即使它不应该被收集!

我见过的替代方法是使用CFRetain-CFRelease这些在我自己的NSObject子类中使用安全吗?我以前只在免费桥接 CF 对象上使用过这些功能。

提供证据的答案加分。我看到disableCollectorForPointer:到处都是推荐的,没有提到这个安全方面。

笔记:

  • 我必须支持垃圾收集,因为此代码将被 PrefPane 用于系统偏好设置。PrefPane 的 64 位版本必须使用垃圾回收。因此,不能选择使用 ARC 或手动引用计数。
  • C API 是 Apple 的(IOKit)之一,所以我也无法控制它。
4

1 回答 1

1

我想我找到了答案。垃圾收集器的源代码似乎不可用,但声明接口的头文件NSGarbageCollector包含NSGarbageCollector.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并运行 as ./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 回答