【问题标题】:Objective C memory management for anonymous objectsObjective C 匿名对象的内存管理
【发布时间】:2009-06-17 02:34:29
【问题描述】:

我正在学习 Objective-C,我正在尝试了解内存管理。我对 C 内存管理相当熟悉,但我试图弄清楚 ObjC 的不同之处。

假设我有一个名为Complex 的类用于保存复数,它有一个方法-(Complex*)add:(Complex*)c; 将传入的复数添加到self(假设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 时创建的临时对象所使用的内存会发生什么变化?我认为这是内存泄漏;这是正确的代码吗?

Complex *c = [[Complex alloc] withReal: -3.0 andImag: -2.4]; // c = -3.0-2.4i
Complex *b = [[Complex alloc] withReal: 1.0 andImag: 2.0]; // b = 1+2i
[c add : b]; // now c = -2.0-0.4i
[b release];

额外的菜鸟问题:Objective-C 2.0 GC 会处理这种情况吗?

【问题讨论】:

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


    【解决方案1】:

    首先,带着我的“最佳实践”帽子,当您创建自定义类时,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 后,对保留/释放/自动释放的调用本质上是无操作的,一旦没有对它们的引用,就会收集对象。一旦指向对象的最后一个指针丢失或超出本地范围,该对象就有资格被收集。 (从技术上讲,GC 只计算 strong 引用,但除非您有意使用弱引用,否则这种区别现在可能无关紧要。)请记住,iPhone 目前不支持垃圾回收。 p>

    【讨论】:

    • 感谢 GC 提示 - 但我还需要一段时间才能开始使用 iPhone ;)
    • 当然可以。如果可以使用 GC,它就很棒,尽管这意味着您必须放弃对 Tiger 的支持,并且您使用的所有框架也必须支持 GC。但随着时间的推移,这将变得越来越普遍,谁知道呢,也许随着 iPhone 变得更快,它们也将开始支持 GC。 :-)
    【解决方案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 ...)

    【讨论】:

      【解决方案3】:

      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]]
      

      玩得开心!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-01-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多