我希望能够在特定的CFType
对象(对于我的当前目的,是一个CGPDFDocument
)被分配、保留、释放或解除分配时记录消息(最好还能中断到调试器)。
由于CGPDFDocument
没有接受CFAllocatorRef
的Create...()
方法,因此我正在尝试暂时更改默认分配器,如下所示:
void MyPDFDocumentCreate()
{
// ...
CFAllocatorRef defaultAllocator = CFAllocatorGetDefault();
CFAllocatorSetDefault(MyLogAllocator());
CGPDFDocumentRef documentRef = CGPDFDocumentCreateWithProvider(provider);
CFAllocatorSetDefault(defaultAllocator);
// ...
}
其中MyLogAllocator()
的定义如下:
static void *(*DefaultAllocate)(CFIndex size, CFOptionFlags hint, void *info);
static const void *(*DefaultRetain)(const void *info);
static void (*DefaultRelease)(const void *info);
void *LogAllocate(CFIndex size, CFOptionFlags hint, void *info)
{
fprintf(stderr, "LogAllocate %p", info);
if (DefaultAllocate)
return DefaultAllocate(size, hint, info);
else
return NULL;
}
const void *LogRetain(const void *info)
{
fprintf(stderr, "LogRetain");
if (DefaultRetain)
return DefaultRetain(info);
else
return info;
}
void LogRelease(const void *info)
{
fprintf(stderr, "LogRelease");
if (DefaultRelease)
DefaultRelease(info);
}
static CFAllocatorRef MyLogAllocator()
{
static CFAllocatorRef theLogAllocator = NULL;
if (!theLogAllocator)
{
CFAllocatorContext context;
CFAllocatorRef defaultAllocator = CFAllocatorGetDefault();
CFAllocatorGetContext(defaultAllocator, &context);
DefaultAllocate = context.allocate;
DefaultRetain = context.retain;
DefaultRelease = context.release;
context.allocate = LogAllocate;
context.retain = LogRetain;
context.release = LogRelease;
theLogAllocator = CFAllocatorCreate(kCFAllocatorUseContext, &context);
}
return theLogAllocator;
}
然而,似乎默认的分配器(据我所知是kCFAllocatorSystemDefault
)在 context.retain 和 context.release 上都为 NULL,因此我没有任何原始实现可以调用。这可能就是为什么当我尝试上面的代码时,会得到以下堆栈跟踪:
#0 0x357ded12 in CFRetain ()
#1 0x357dcb68 in _CFRuntimeCreateInstance ()
#2 0x303fe35e in CGTypeCreateInstanceWithAllocator ()
#3 0x303fe34c in CGTypeCreateInstance ()
#4 0x304b32f4 in CGPDFDocumentCreateWithProvider ()
#5 0x000293f4 in MyPDFDocumentCreate ([...]) at [...]
XCode并没有告诉我它为什么停止运行,但是如果我尝试继续运行,会出现以下提示:
(gdb) continue
Continuing.
Program received signal SIGTRAP, Trace/breakpoint trap.
0x357ded12 in CFRetain ()
(gdb) continue
Continuing.
Program received signal SIGTRAP, Trace/breakpoint trap.
0x357ded12 in CFRetain ()
(gdb)
无论我尝试多少次,都会得到相同的SIGTRAP错误。我不知道如何解释它;我设置的唯一断点是在objc_exception_throw
上的符号断点。
需要注意的一点是,在CFAllocatorCreate()
中,每个LogRetain()
和LogAllocate()
都成功调用了一次(按顺序):
#0 LogRetain (info=0x1a8000) at [...]
#1 0x358086f2 in CFAllocatorCreate ()
#2 0x00028d58 in MyLogAllocator () at [...]
#3 0x000293e0 in MyPDFDocumentCreate ([...]) at [...]
#0 LogAllocate (size=104, hint=0, info=0x1a8000) at [...]
#1 0x3580882e in CFAllocatorCreate ()
#2 0x00028d58 in MyLogAllocator () at [...]
#3 0x000293e0 in MyPDFDocumentCreate ([...]) at [...]
然后LogAllocate()
再次从CFAllocatorAllocate()
中成功分配内存:
#0 LogAllocate (size=64, hint=1024, info=0x1a8000) at [...]
#1 0x357dcc06 in CFAllocatorAllocate ()
#2 0x357dcb04 in _CFRuntimeCreateInstance ()
#3 0x303fe35e in CGTypeCreateInstanceWithAllocator ()
#4 0x303fe34c in CGTypeCreateInstance ()
#5 0x304b32f4 in CGPDFDocumentCreateWithProvider ()
#6 0x000293f4 in MyPDFDocumentCreate ([...]) at [...]
在#2处调用出现问题的
CFRetain()
之前,需要调用_CFRuntimeCreateInstance()
。请问有人能帮我理解这里发生了什么(特别是默认分配器如何处理保留和释放以及我为什么会收到SIGTRAP)?如何修复它?是否有更好的方法来做我试图做的事情?
(我想我可能可以弄清楚如何使用DTrace来探测由
CGPDFDocument
的CFTypeID
过滤的CFRetain()
和CFRelease()
,但我不知道要探测什么进行释放操作(跟踪分配不那么重要,因为我知道它是在CGPDFDocumentCreateWithProvider()
内完成的)。另外,我更愿意能够在保留/释放/解除分配时中断到调试器,我认为使用DTrace是不可能的。)更新:现在已阅读了
CFRelease
的源代码,我意识到我误解了context.retain
和context.release
的目的--它们用于保留和释放context.info
。因此,上述整个方法都行不通。但是,也许DTrace/Instruments巫师仍然可以施魔法?!