2

考虑这样一个函数:

void* getData()
{
    void* data= malloc(32);
    NSData* __autoreleasing dataObject= [NSData dataWithBytesNoCopy: data length: 32 freeWhenDone: YES]
    return data;
}

如果我尝试执行此代码并在返回的内存区域中打印值(尽管它们没有初始化,但只是为了进行测试),我不会得到任何异常。

但是,如果我尝试放置一个断点并查看活动对象的映射,则没有活动的 NSData 对象,尽管数据仍在堆中,为什么?

我会知道如何返回一个自动释放的对象,就像在 ARC 之前一样。ARC 处理所有事情,但在这种情况下,数据被释放,因为我退出了函数范围。如何在通话后使其活跃并自动释放?

4

2 回答 2

3

这是不可能的。ARC 旨在自动释放不可访问的对象。由于 NSData 对象是本地的并且没有返回,所以它不能被使用,所以 ARC 将它释放。

从您发布的代码来看,您似乎希望返回指向某些数据的指针,即使它不是objective-c 对象,也会自动释放这些数据。您可能还希望能够使用生成它的函数内的 NSData API 访问这些数据。您在这里有几个选择。

  1. 更改您的代码以返回 NSData 对象。如果调用代码想要直接访问缓冲区,它可以使用 NSData 的bytes方法。但是,如果您的代码是从 C 调用的,这将不起作用。

  2. 停止尝试自动释放 C 数据。好的 C 代码应该知道何时需要释放数据,所以这应该不是问题。只需将您的代码更改为freeWhenDone: NO并在处理完数据后使用调用代码free()

  3. 将此函数放在其自己的文件中并禁用该文件的 ARC。这允许您通过手动调用来做您想做的事情autorelease,但您还必须手动处理函数其余部分的引用计数。

  4. 结合 1 和 3。有一个使用 ARC 并返回 NSData 对象的函数,以及一个没有 ARC 的包装函数,它调用第一个获取自动释放的对象,然后从结果中返回缓冲区指针。

于 2013-01-06T20:48:14.250 回答
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

然后我测试了 release,稍微改变了测试,添加了一个设置在的__weak全局变量,g_dataautoreleaseBufferOfLength

__weak NSData *g_data = nil;
NSData *autoreleaseBufferOfLength(void *bytes, NSUInteger length) 
{ 
    return (g_data = [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:YES]); 
}  

并更改主登录:

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 对象并将其放置在自动释放池中的效果。

注意:这不是一个好主意。正如@ughoavgfhw 在他的回答中所描述的那样,通过排空自动释放池来实现malloc'd的最佳方法free是添加一个在没有 ARC 的情况下构建的文件(使用)。-fno-objc-arc然而,这种方法可能会引起一些兴趣。

于 2013-01-06T22:29:41.717 回答