我正在学习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会处理这种情况吗?
答案 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]]
玩得开心!