我希望用我自己的实现替换iPhone应用中的默认CFAllocator
。我想控制UIWebView
分配的内存,因为它在加载网站后似乎保留了这么多内存,并且在释放UIWebView
时内存仍然存在。
在我致电CFAllocatorSetDefault
后,我在下次分配时遇到EXC_BREAKPOINT
例外。
异常似乎发生在对CFRetain
的调用中(在模拟器中完成但在设备上发生同样的事情):
CoreFoundation`CFRetain:
0x1c089b0: pushl %ebp
0x1c089b1: movl %esp, %ebp
0x1c089b3: pushl %edi
0x1c089b4: pushl %esi
0x1c089b5: subl $16, %esp
0x1c089b8: calll 0x1c089bd ; CFRetain + 13
0x1c089bd: popl %edi
0x1c089be: movl 8(%ebp), %esi
0x1c089c1: testl %esi, %esi
0x1c089c3: jne 0x1c089db ; CFRetain + 43
0x1c089c5: int3
0x1c089c6: calll 0x1d66a00 ; symbol stub for: getpid <- EXC_BREAKPOINT (code=EXC_I386_BPT subcode=0x0)
0x1c089cb: movl %eax, (%esp)
0x1c089ce: movl $9, 4(%esp)
0x1c089d6: calll 0x1d66a4e ; symbol stub for: kill
0x1c089db: movl (%esi), %eax
0x1c089dd: testl %eax, %eax
0x1c089df: je 0x1c08a17 ; CFRetain + 103
0x1c089e1: cmpl 1838519(%edi), %eax
0x1c089e7: je 0x1c08a17 ; CFRetain + 103
0x1c089e9: movl 4(%esi), %ecx
0x1c089ec: shrl $8, %ecx
0x1c089ef: andl $1023, %ecx
0x1c089f5: cmpl 1834423(%edi,%ecx,4), %eax
0x1c089fc: je 0x1c08a17 ; CFRetain + 103
0x1c089fe: movl 1766575(%edi), %eax
0x1c08a04: movl %eax, 4(%esp)
0x1c08a08: movl %esi, (%esp)
0x1c08a0b: calll 0x1d665c8 ; symbol stub for: objc_msgSend
答案 0 :(得分:5)
Core Foundation有一个使CFAllocatorSetDefault
无效的错误。
具体来说,如果您在CFRuntime.c
中研究_CFRuntimeCreateInstance
的实施,您会看到:
NULL
作为allocator
参数传递,它将尝试保留NULL
而不是当前的默认分配器。CFRetain
的调用因此会崩溃。 应该做的是当NULL
作为allocator
参数时,保留当前的默认分配器。
由于Apple自己的库中的许多函数显然将NULL
(或kCFAllocatorDefault
(也是空指针)传递给创建Core Foundation对象的函数,因此如果快速崩溃,则必然会很快崩溃。你根本就改变了默认的分配器。
我的测试用例:我创建了一个新的单视图iPhone应用程序。我在main
添加了一行:
int main(int argc, char *argv[])
{
CFAllocatorSetDefault(kCFAllocatorMalloc);
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
应用程序在模拟器和我的测试设备上启动时在CFRetain
中使用EXC_BREAKPOINT
崩溃,并使用空指针作为函数参数。
您正在将空指针传递给CFRetain
。如果这与您的自定义分配器有关,则需要发布更多详细信息,例如发生异常时的完整调用堆栈。
在您的反汇编列表中,0x1c089b0
到0x1c089bd
的说明是功能序言。
在0x1c089be
,movl 8(%ebp), %esi
指令将堆栈中的第一个函数参数加载到%esi
。
在0x1c089c1
,testl %esi, %esi
指令根据%esi
的值设置处理器标志。特别是,如果%esi
包含零,它会将Z(零)标志设置为1,如果%esi
包含其他任何内容,则将Z标志设置为0。
在0x1c089c3
,如果jne 0x1c089db
条件为真,则ne
指令跳转。当Z标志为0时ne
条件为真,而当Z标志为1时为假。因此当%esi
(第一个参数)非零时该指令跳转,并且当%esi
时0x1c089c5
条件跳转1}}为零。
在int3
,SIGTRAP
指令会引发EXC_BREAKPOINT
信号,异常代码为int3
。设置断点时,调试器通常会将CFRetain
指令填充到程序中。在这种情况下,它在编译时在程序中进行了硬编码。
因此,您将获得此异常,因为您将空指针传递给CFRetain
。
如果您愿意,还可以查看CFTypeRef CFRetain(CFTypeRef cf) {
if (NULL == cf) { CRSetCrashLogMessage("*** CFRetain() called with NULL ***"); HALT; }
if (cf) __CFGenericAssertIsCF(cf);
return _CFRetain(cf, false);
}
的源代码。它位于CFRuntime.c
:
CFRetain
所以NULL
做的第一件事就是测试它的参数是否为CGSetCrashLogMessage
。 HALT
是CoreFoundation_Prefix.h
中定义的宏,不执行任何操作。 #define HALT do {asm __volatile__("int3"); kill(getpid(), 9); } while (0)
是CFInternal.h
中定义的宏:
HALT
如您所见,int3
有一条硬编码的kill(getpid(), 9)
指令。然后它调用{{1}}。这与您的反汇编列表相符。