1

在我的 Cocoa 项目中,我有很多使用 malloc/free 的地方。然而几个月前我决定重构以利用 ARC,为了做到这一点,我尝试替换 malloc 它将返回指向将自动清理的东西的指针。

我使用了这个功能(错误检查和其他日志被省略)

+ (void *) MallocWithAutoCleanup: (size_t) size 
{
    NSMutableData * mutableData = [[NSMutableData alloc] initWithLength:size];
    void * data = [mutableData mutableBytes];
    return data;
}  

这工作了一段时间,但最近出现了随机内存覆盖问题。我追查了这个函数的原因,似乎正在发生的事情是 NSMutableData 实例正在被释放,即使我保留了一个指向它的 mutableBytes 的指针。

我猜这是因为对对象的唯一直接引用是本地的并且正在消失,而 mutableBytes 指向对象内部,因此 ARC 不够聪明,无法处理这种引用计数。

只要使用 mutableBytes 指针(即有人引用它),有什么方法可以重构此代码以保留 mutableData 对象?我知道一种选择是只返回 NSMutableData 本身,但这需要进行大量重构并且看起来非常混乱。

4

2 回答 2

9

在 10.7 SDK 中,-[NSMutableData mutableBytes]是用NS_RETURNS_INNER_POINTER属性修饰的。这向编译器发出信号,该方法返回一个指针,其有效性取决于仍然存在的接收器。ARC 对此所做的究竟是什么可以改变,但目前它保留并自动释放接收器(受优化掉冗余操作的影响)。

因此,指针在当前自动释放池的生命周期内有效。这类似于-[NSString UTF8String](以相同的方式装饰)。

只要有对字节指针的任何引用,ARC 就无法保持可变数据对象的活动状态。ARC 不是垃圾收集器。它不会监视所有指针的所有用途。它在本地运行。它检查一个给定的函数、方法或块,并根据命名约定为代码的行为发出保留和释放。(请记住,ARC 可以与未使用 ARC 支持编译的代码互操作。)

由于 avoid*不是对象指针并且不能被保留或释放,ARC 不能用它做任何事情。因此,在调用您的-MallocWithAutoCleanup:方法的代码中,ARC 看不到它可以管理的任何内容。它不会发出任何特殊的内存管理代码。(此时它会发出什么)在编译调用者时,编译器可能对方法实现或其中的可变数据对象一无所知。

Think about it another way: if you were still writing manually reference counting code, what would you do in the caller of your method to keep the pointer valid? For the most part (ignoring __weak references), all ARC does is automatically do what you would do manually. Once you consider that you would have no options in that case, you realize that neither does ARC.

于 2012-05-31T21:41:00.777 回答
2

我想你回答了你自己的问题。如果要用于NSData管理通用内存分配,则需要保留对NSData对象的引用,直到使用完它拥有的内存为止,此时您对相关 NSData 对象的引用为零。与手动释放分配的内存相比,这似乎没有任何优势。就个人而言,我会继续显式地使用 malloc()/free() 而不是试图以 ARC 管理分配内存的方式扭曲我的代码。

要么这样,要么我会编写我的代码,这样它就不必首先使用 malloc/free。我会说典型的“纯”Cocoa 项目没有很多(如果有的话)显式 malloc() 调用,除非有充分的理由,否则我会对这样做的代码有点怀疑。你为什么首先使用 malloc() ?

于 2012-05-31T20:49:16.630 回答