考虑像这样的函数:
void* getData()
{
void* data= malloc(32);
NSData* __autoreleasing dataObject= [NSData dataWithBytesNoCopy: data length: 32 freeWhenDone: YES]
return data;
}
如果我尝试执行此代码并在返回的内存区域中打印值(虽然它们没有初始化,但只是为了进行测试),我没有任何异常。
但是如果我尝试设置断点并查看生命对象的地图,虽然数据仍在堆中,但是没有NSData对象存活,为什么?
我会知道如何返回一个自动释放的对象,就像ARC之前一样。 ARC处理所有内容,但在这种情况下,数据被释放,因为我退出了函数范围。如何在通话后让它活着并自动释放?
答案 0 :(得分:3)
这是不可能的。 ARC旨在自动释放无法访问的对象。由于NSData对象是本地的而且没有返回,因此无法使用它,因此ARC将其解除分配。
根据您发布的代码判断,您似乎想要返回指向某些数据的指针,这些数据将自动释放,即使它不是Objective-c对象。您可能还希望能够使用生成它的函数内部的NSData API来访问此数据。你有几个选择。
更改代码以返回NSData对象。如果调用代码想要直接访问缓冲区,则可以使用NSData的bytes
方法。但是,如果从C调用代码,则无法执行此操作。
停止尝试自动释放C数据。好的C代码应该知道何时需要释放数据,所以这应该不是问题。只需将代码更改为freeWhenDone: NO
,并在完成数据后让调用代码使用free()
。
将此函数放在自己的文件中,并为该文件禁用ARC。这允许您通过手动调用autorelease
来执行您想要的操作,但您还必须手动处理函数其余部分的引用计数。
组合1和3.有一个使用ARC的函数并返回NSData对象,而没有ARC的包装函数调用第一个获取自动释放的对象,然后从结果中返回缓冲区指针。
答案 1 :(得分:2)
一种方法是添加第二个功能
NSData *autoreleaseBufferOfLength(void *bytes, NSUInteger length)
{
return [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:YES];
}
此函数将返回一个自动释放的NSData
对象。
从缓冲区返回函数
中调用此函数void *getAutoreleasedBuffer(NSUInteger length)
{
void *buffer = malloc(length);
if (buffer) {
autoreleaseBufferOfLength(buffer, length);
}
return buffer;
}
从NSData
返回的autoreleaseBufferOfLength
对象位于自动释放池中,因此在自动释放池耗尽时将释放缓冲区(因为在NSData
中创建的autoreleaseBufferOfLength
对象然后解除分配。)
我使用此main
函数
int main(int argc, const char * argv[])
{
@autoreleasepool {
__unused void * buffer = autoreleasingBuffer(32);
NSLog(@"%s:exiting @autoreleasepool block", __PRETTY_FUNCTION__);
}
NSLog(@"%s:exited @autoreleasepool block", __PRETTY_FUNCTION__);
return 0;
}
并在-[NSConcreteData dealloc]
处添加带有操作po @"deallocating NSConcreteData"
的符号断点,并选中“评估后自动继续”。这是输出
[53030:303] int main(int, const char **):exiting @autoreleasepool block
(NSString *) $0 = 0x0000000100300530 deallocating NSConcreteData
[53030:303] int main(int, const char **):exited @autoreleasepool block
然后我测试了发布,稍微更改了测试,添加了__weak
全局变量g_data
,该变量在autoreleaseBufferOfLength
中设置,
__weak NSData *g_data = nil;
NSData *autoreleaseBufferOfLength(void *bytes, NSUInteger length)
{
return (g_data = [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:YES]);
}
并更改main中的日志记录:
int main(int argc, const char * argv[])
{
@autoreleasepool
{
__unused void * buffer = autoreleasingBuffer(32);
NSLog(@"%s:exiting @autoreleasepool block; g_data = %@", __PRETTY_FUNCTION__, g_data);
}
NSLog(@"%s:exited @autoreleasepool block; g_data = %@", __PRETTY_FUNCTION__, g_data);
return 0;
}
在构建发布和运行时,这是输出:
[53934:707] int main(int, const char **):exiting @autoreleasepool block; g_data = 00000000 00000070 00000000 00000070 10000000 00000000 00000000 00000000>
[53934:707] int main(int, const char **):exited @autoreleasepool block; g_data = (null)
这些测试表明在第二个函数dataWithBytesNoCopy:length:freeWhenDone:
中包含对autoreleaseBufferOfLength
的调用会产生创建NSData对象并将其放入自动释放池的效果。
注意:这是 NOT 一个好主意。通过自动释放池的耗尽实现malloc
'd free
'的最佳方法是添加一个没有ARC(使用-fno-objc-arc
)构建的文件,如@ ughoavgfhw在他的回答中描述。然而,这种方法可能有一些兴趣。