匿名对象的Objective C内存管理

时间:2009-06-17 02:34:29

标签: objective-c memory-management memory-leaks

我正在学习Objective-C,我正在尝试了解内存管理。我对C内存管理非常熟悉,但我想弄清楚ObjC是多么不同。

假设我有一个名为Complex的类,用于保存复数,其中有一个方法-(Complex*)add:(Complex*)c;,将传递的复数加到self({{1} }是一个可变对象,让我们说)。

所以我可以这样称呼它:

Complex

用于Complex *c = [[Complex alloc] withReal: -3.0 andImag: -2.4]; // c = -3.0-2.4i [c add : [[Complex alloc] withReal: 1.0 andImag: 2.0]]; // now c = -2.0-0.4i 调用中创建的临时对象的内存会发生什么变化?我认为这是一个内存泄漏;这是正确的代码吗?

add

Bonus noob问题:Objective-C 2.0 GC会处理这种情况吗?

3 个答案:

答案 0 :(得分:2)

在第一个例子中:

[c add : [[Complex alloc] withReal: 1.0 andImag: 2.0]];

您已经为引用计数1分配了对象并且它没有被释放,因此它是内存泄漏,因为您没有将指针存储到该对象,因此您无法向其发送释放。

您可以修改它以自动释放'临时'对象:

[c add : [[[Complex alloc] withReal: 1.0 andImag: 2.0] autorelease]];

您可能会在名称中使用init来指出该对象未自动释放且用户必须处理内存管理这一事实:

initWithReal:(double)r andImag:(double)i

第二个例子是正确的。

Cocoa提供了许多返回自动释放对象的方法(静态和成员),例如[NSString stringWithString:@"example"],因此您可以创建返回自动释放对象的方法,这样您就不必处理释放:

Complex * c = [Complex complexWithReal:1.0 andImag:2.0]

您还可以创建获取原始数据的方法,例如像addReal:(double)r andImag:(double)i之类的东西,所以你可以跳过alloc / init步骤。

另一种选择是使用Cocoa中使用的结构(CGRect等)。它们与C中的相同意味着它们在堆栈上分配,一旦它们超出范围,它们就会消失,而不是使用alloc / init在堆上分配的对象。适用于经常使用的“小”物品,但通常你不想让它们保持在周围。

至于GC我没有看到任何理由为什么GC无法处理释放对象 - 我没有太多使用它(只看了几个例子 - 我更喜欢自己管理内存 - 除非使用python ...)

答案 1 :(得分:2)

首先,在我的“最佳实践”的基础上,当您创建自定义类时,Objective-C习惯用于使用前导“init”命名初始值设定项(相当于其他语言中的“构造函数”)。另外,尽量不要缩写方法选择器的部分或命名参数。

例如,而不是:

- (id) withReal:(double)r andImag:(double)i { ... }

你应该考虑这样做:

- (id) initWithReal:(double)real imaginary:(double)imaginary { ... }

第二部分之前的“和”是品味问题 - 使用它是完全可以接受的,但我更喜欢它,因为你可能不会将它用于具有3+参数的方法。例如,如果您要创建RGB颜色以使用-initWithRed:andGreen:andBlue:,那么它将比必要更详细 - 通常更喜欢写/使用-initWithRed:green:blue:


对于你的实际问题,我完全同意创建一个便利类构造函数是这些情况的明智之举。每次需要一次性实例时,它在视觉上比alloc-init-autorelease更清晰。匹配上面的更改,我会调整@ kent的答案,看起来像这样:

+ (Complex*) complexWithReal:(double)real imaginary:(double)imaginary {
    return [[Complex alloc] initWithReal:real imaginary: imaginary] autorelease];
}

对于奖金问题,是的,Objective-C 2.0的GC会处理这个问题;最好写的保留释放代码在GC下按原样运行,特别是如果你不再需要它们时要小心将指针设置为nil。启用GC时,对retain / release / autorelease的调用基本上是no-ops,并且一旦没有对它们的引用就会收集对象。一旦指向对象的最后一个指针丢失或超出本地范围,该对象就有资格进行收集。 (从技术上讲,GC只计算引用,但除非你故意使用弱引用,否则这种区别可能与现在无关。)请记住,目前iPhone上不支持垃圾收集。

答案 2 :(得分:1)

stefanB是对的。因为你将会做很多这样的'动态'实例化(我会假设......),构建一个静态的“便利”构造函数是有意义的(在appKit& co中有很多例子)

+(Complex*) complexWithReal:(double)r andImag:(double)i
{
  return [[Complex alloc] initWithReal:r andImag:i] autorelease];
}

这将使您调用添加消息,然后看起来像这样:

[c add : [Complex complexWithReal:2.0 andImage:1.0]]

玩得开心!