在我的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不够智能以处理该类型引用计数。
有没有什么办法可以重构这段代码来保留mutableData对象,只要有人使用mutableBytes指针(即某人有引用它)?我知道一个选择就是返回NSMutableData本身,但这需要一些重构并且看起来非常混乱。
答案 0 :(得分:9)
在10.7 SDK中,-[NSMutableData mutableBytes]
使用NS_RETURNS_INNER_POINTER
属性进行修饰。这向编译器发出信号,表明该方法返回一个指针,其有效性取决于仍然存在的接收器。 ARC对此做了什么是可以改变的,但目前它保留并自动释放接收器(需要优化冗余操作)。
因此,指针在当前自动释放池的生存期内有效。这类似于-[NSString UTF8String]
(以相同的方式装饰)。
只要对字节指针有任何引用,ARC就无法保持可变数据对象的存活。 ARC不是垃圾收集器。它不会监视所有指针的所有用途。它在当地运营。它检查一个给定的函数,方法或块,并根据命名约定发出代码行为的保留和释放。 (请记住,ARC可以与尚未使用ARC支持编译的代码互操作。)
由于void*
不是对象指针而无法保留或释放,因此ARC无法对其执行任何操作。因此,在代码调用您的-MallocWithAutoCleanup:
方法中,ARC看不到它可以管理的任何内容。它不会发出任何特殊的内存管理代码。 (可以在那时发出什么?)在编译调用者时,编译器可能对方法实现或其中的可变数据对象一无所知。
以另一种方式思考:如果您仍在手动编写引用计数代码,那么您在方法的调用者中如何保持指针有效?在大多数情况下(忽略__weak
引用),所有ARC都会自动执行您手动执行的操作。一旦你认为在这种情况下你没有选择,你就会意识到ARC也没有。
答案 1 :(得分:2)
我想你回答了自己的问题。如果要使用NSData
来管理通用内存分配,则需要保留对NSData
对象的引用,直到完成它拥有的内存为止,此时您将无法引用(s)有问题的NSData对象。与仅手动释放malloced内存相比,这似乎没有提供任何优势。就个人而言,我会继续显式地使用malloc()/ free(),而不是试图以一种ARC管理malloced内存的方式来扭曲我的代码。
或者,或者我编写代码使得它不必首先使用malloc / free。我会说典型的“纯粹”Cocoa项目没有很多(如果有的话)显式的malloc()调用,除非有充分的理由,否则我会对代码有点怀疑。你为什么首先使用malloc()?