在Objective-C/iPhone应用程序中使用“经典的”malloc()/free()函数是否可行?

56

我已经尝试了一段时间的iPhone开发,虽然对于一个“铁杆”的.NET开发人员来说有点尴尬,但一旦你习惯了它,就不那么糟糕了。

在我读过的所有关于Objective-C的书籍中,只有关于retain/release(引用计数)的内存管理讨论。作为一个老派的C/C++开发人员,使用malloc()free()通常是分配内存的“标准”方式,这看起来有些奇怪只有在一些脚注中才被提到。

我知道malloc()free()在Objective-C中也可以使用,但我想知道这是否是常见做法。毕竟,如果我想分配一个包含100个整数的数组,这似乎是最有效的方法:

int *array = malloc(sizeof(int) * 100);

memset(array,0,sizeof(int) * 100);

// use the array

free(array);

这确实是最好的方法吗?还是我应该避免使用纯 C 的内存管理?

5个回答

87

我经常使用一个Objective-C封装的原始内存对象来完成类似的任务:NSMutableData。它有一个优点,可以让你拥有retain/release的所有权,并且可以轻松地扩展数组(无需自己进行realloc操作)。

你的代码将如下所示:

NSMutableData* data = [NSMutableData dataWithLength:sizeof(int) * 100];
int* array = [data mutableBytes];
// memory is already zeroed

// use the array

// decide later that we need more space:
[data setLength:sizeof(int) * 200];
array = [data mutableBytes]; // re-fetch pointer in case memory needed to be copied

// no need to free
// (it's done when the autoreleased object is deallocated)

我喜欢@Nikolai Ruhe这个答案。我将一个“malloc”数组用作属性。我有三个这个类的对象,并在dealloc中释放了数组。但是,在释放第一个对象后,当释放第二个对象时,出现了“malloc:***错误,指针被释放未分配”的错误!看起来malloc是在“类级别”而不是“对象级别”完成的。切换到您的解决方案使此问题消失,但对象的“行为”也发生了变化。还不确定为什么。 - iPadDeveloper2011
5
@iPadDeveloper2011 你需要重新阅读《内存管理编程指南》。链接在http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html - Nikolai Ruhe
1
@iPadDeveloper2011:你需要加强对内存管理的理解。在这种情况下,数据被自动释放了。只要自动释放池没有被清空,这段代码就没问题,但是一旦你进入可能会清空自动释放池的作用域,你就不能再使用这个数据了。如果你需要它存在更长时间,那么你必须retain它,然后在使用完后release它。 - Adam Rosenfield
3
我个人喜欢这个答案。我一直很烦恼当我需要使用整数数组时失去了引用计数,并且无法轻松扩展它们。感谢提供非常好的解释和示例! - Accatyyc
这可能是处理此类问题的最佳方式。 - Cameron Lowell Palmer
显示剩余7条评论

48

完全没问题——Objective-C 是 C 语言的严格超集,因此如果你想编写纯 C 代码,没有任何阻止你这样做。在许多情况下,使用 mallocfree 可以避免 Objective-C 运行时的开销,这是有利的。

例如,如果你需要动态分配一个未知数量的整数数组,使用 mallocfree 通常更简单更容易:

int *array = malloc(N * sizeof(int));  // check for NULL return value!
// use array[0]..array[N-1]
...
free(array);

对比:

NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:N];
// use NSMutableArray methods to do stuff with array; must use NSNumbers instead
// of plain ints, which adds more overhead
...
[array release];

我正在为iPhone开发一个文字游戏,我们需要加载一个数兆大小的有效单词字典。这个单词列表被加载到一个使用malloc()函数分配的巨大的char数组中,并且采用了一些聪明的优化以进一步减小内存大小。显然,对于像这样的东西,使用NSArray的开销在受限制的iPhone上完全是不切实际的。我不知道具体的开销是多少,但肯定比每个字符一个字节还要多。


6
关于“严格超集”的问题。虽然我个人(强烈)同意你的观点,但由于我们正在讨论苹果/ iPhone开发,这种说法在技术上并不正确。苹果公司定义如下:“Objective-C语法是GNU C / C ++语法的超集”。 “严格超集”有一个非常具体的意义,未经限定的“超集”是两者中较小的那个(类似于“行为类似”与“完全相同”之间的区别)。 修饰语“语法”的限定进一步将其限制到几乎没有用处的程度,有效地将苹果公司对C99规范的义务限制为该标准中仅有的16页内容,占总页数552页的极少部分。 - johne
我在使用Objective C中的malloc/free时遇到了一些问题。请参见我对Nikolai答案的评论。除此之外,我还遇到了将指向malloc数组的指针分配(复制)给其他对象以共享malloc数组的问题。 - iPadDeveloper2011
虽然你可以使用malloc()和free(),但是通过使用适当长度的NSMutableData,你可以避免大部分运行时开销。 - Steven Fisher

5
当然,您可以使用这些函数,因为Objective-C只是C的超集。但是,这种做法相当不常见,因为Objective-C包含对象和使其更容易的方法。
毕竟,您可以将上述代码编写为:
NSMutableArray *array = [[NSMutableArray alloc] init];

//Use the array, adding objects when need be

[array release];

虽然你必须创建NSNumber对象来存储int(因为NSArray不允许添加非对象类型),但通常更常使用对象,因为它更容易移动数据,并且数组类与其他Cocoa类更常集成,内存管理通常比标准的C语言内存管理更简单明了。
此外,如果你开始向数组中添加或删除对象,则Cocoa数组对象会使这个过程变得更加容易。

2
如果你只需要一个简单的整数数组,那么这似乎有些过度了。特别是创建NSNumber对象的需求对我来说似乎很低效。如果我想分配一个包含10万个布尔值的数组怎么办? - Philippe Leybaert
也许,与使用简单的整数数组相比,会有一些开销。但是它们肯定比使用C内存管理更常用。如果您正在分配100,000个布尔值的数组,那么可能有比您当前实现更好的方法(除非这是一个假设的情况)。 - Alex Rozanski
1
如果你正在处理非常简单的对象,这样做就显得有些过度了。例如,如果你要为iPhone构建扫雷游戏,使用结构体并malloc一个结构体数组比创建对象并将它们放入NSArray中要快得多,而且还会使用更少的内存。 - Dave DeLong
当然,这取决于问题的上下文,但使用标准的C内存管理是相当不常见的。另外,正如我所说,如果您正在操作数组项,则它们可能会变得更加有用。 - Alex Rozanski
我也不同意这不是常见的说法。这非常取决于问题域。例如,当使用C库(如OpenGL,非常常见)时,您必须深入了解C内存管理。此外,对于大型数组pod类型,它更加高效。 - Nikolai Ruhe
1
只是想指出,一个由 100,000 个布尔值(BOOLs?)组成的数组已经是一种内存效率低下的解决方案了,因为每个 BOOL 占用 1 字节的空间,但实际上你只需要 1 个比特。因此,使用由 100,000/8 个字符组成的数组和位运算符会更好,大约可以提高 8 倍的效率。 - iPadDeveloper2011

3

如果你处理的是标准C类型,那么在Objective-C中这也是很常见和“可以接受”的。在C语言中就是这样做的,而Objective-C是C语言的一部分。

在Cocoa中,也不少见地会写一些对象包装器来将其与 Cocoa 其他部分协调起来(如KVO、内存管理等)。因此,你可以创建一个IntArray类,它在幕后执行malloc操作,以便根据需要保留并释放它。请注意,这并非绝对必要——如果这种结构不是你程序的主要部分,这仅仅是方便。


2

使用malloc和free进行自己的内存管理是完全可以的。实际上,NSObject的allocWithZone:方法使用malloc来获取内存。


1
技术上讲应该是calloc,但是没错。=) - Dave DeLong
7
实际上,从技术上讲是NSAllocateObject()。接下来发生的事情更为复杂。在启用GC的ObjC2中,NSAllocateObject()调用objc_allocate_object()。在未启用GC的ObjC2或ObjC < 2中,NSAllocateObject()调用class_createInstanceFromZone(),后者又调用malloc_zone_calloc(),其逻辑等同于calloc()。使用count为1的calloc()分配的空间,根据定义与通过malloc分配相同size的空间不可区分,且已将其所有位初始化为零(C99 7.20.3.1.2)。 - johne

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接