2

我已经使用 ojective c 快一周了,我主要是一个 c++ 编码器。在我阅读了苹果的内存管理指南之后,我尝试将我在 c++ 中的内存使用风格带入客观的 c... 我试图总结这些场景,如果我按照这些说明进行操作,我想我不会犯内存错误。如果我错了,请告诉我:)

我会尽量不使用自动释放,个人来说,使用自动释放,在某些自动释放池被耗尽之前,可能总会有一些冗余内存。我只会使用 release,它确保我的应用程序在任何时候都使用最少的内存。

Apple 说的另一件事,我用我自己的话来描述的是:每次我添加一个保留/分配/复制时,我都应该在某个地方添加一个发布

以下是我总结的所有场景:

  1. 在同一个函数中:分配一个对象,使用它,然后释放

  2. 在类的init函数中,分配一个对象,在类的dealloc函数中,释放对象

  3. 当需要拥有一个指针时,应该在类的方法中保留一个输入指针(比如说方法 A),然后在类的dealloc函数中释放指针。

    我发现在objective c中使用retain的时机和在c/c++中使用memcpy的时机是一样的,所以我把retain当作“内存高效副本”

    如果输入的保留指针要设置为成员指针变量,则应先释放成员指针。所以在case[3]中,类的init中的alloc与方法A中的release配对,方法A中的retaindealloc中的release配对

  4. 返回一个指针作为返回值。老实说,当我使用 c++ 时,我从不做这样的事情。如果返回成员指针也没关系,因为有人会处理它:

    -(UIButton*) getTheButton() {
        return theButton;
    }
    

    但是返回一个指向本地分配对象的指针真的很糟糕:

    -(UIButton*) getTheButton() {
        UIButton* myButton = [[UIButton alloc] init];
        return myButton; //TERRIBLE!
    } 
    

    有人可能会说我应该在这种情况下使用自动释放,但我只是想通过使用这个来绕过该解决方案:我只会返回成员指针,或者我不会返回指针,我只会对给定的输入指针进行操作。

    -void operateOnTheButton(UIButton* button) {
        [button release];
        button = [[UIButton alloc] init];
    } 
    

因此,如果我按照上面的内存使用说明进行操作,请告诉我是否有任何问题。

感谢:D

4

5 回答 5

9

在一个重要的场景中,您必须使用autorelease. 如果您分配/保留/复制一个对象,然后将其返回给您拥有该对象的其他代码,但您不能release这样做,因为其他代码需要使用它(或者您不会返回它)。

在这种情况下,您必须通过向对象发送autorelease消息来对对象的释放负责,从而确保它最终会被释放。这正是像 NSString 这样的基础方法stringWithString:所做的。您不必release从该方法获得的字符串对象,因为您没有分配/保留/复制它,但如果该方法没有为您自动释放它,它将继续存在[pool drain]并成为内存泄漏。

确保您了解引用计数的工作原理。release减少对象的引用计数。当计数达到 0 时,对象被销毁。alloccopy创建一个引用计数为 1的对象。retain将接收对象的引用计数增加 1。对于每个分配/保留/复制,必须有一个release. 1上升,1下降。

因此,当您返回您创建的对象并将控制权传递给被调用者时,您会失去 1 对 1 等式的平衡,因为您无法做到release这一点。这就是为什么有autorelease. 它不会减少接收者的 ref 计数器,因此不会有被破坏的危险,但是当池耗尽时,池中的每个对象都会收到一条release消息,autorelease从而恢复平衡(除非有意保留一个对象所以它会在排水管中幸存下来,在这种情况下,被调用者稍后需要release它)。

至于第 4 点,您的第二个示例很好,如果您autorelease在基础类中使用这种技术的示例比比皆是。每个数据类,如 NSString、NSArray、NSDictionary 等,都有返回本地分配的对象指针的方法。它们通常具有thisWiththat:类似stringWithstring:或的形式dictionaryWithContentsOfFile:。后者将分配一个字典对象,用文件的内容填充它,自动释放它,并返回一个指针给你。它被称为工厂函数。obj-c 中非常常见的模式,java 也是。

您的第三个代码示例稍后会导致运行时错误。您正在释放一个您不拥有的对象。所有者不会期望您这样release做,这可能意味着它将收到太多release消息,从而导致运行时错误。如果你没有分配/保留/复制它,你就不拥有它,所以你不应该释放它。

于 2010-07-23T02:51:52.610 回答
2

自动释放部分不正确。如果你做一个工厂方法,它不是以 alloc/retain/copy 开头的。你必须自动释放它。调用者不创建对象,所以调用者不能也不应该释放它。因此,如果调用者想要做某事并确保对象存在,调用者必须自己保留它们。

好的解决方案是:

-(UIButton*) getTheButton() {
    UIButton* myButton = [[UIButton alloc] init];
    return [myButton autorelease];  // if you don't do autorelease, memory will leak
} 

然后在调用者中:

- (void)caller {
  UIButton *button = [[self getTheButton] retain];
}

你不可以做这个:

-void operateOnTheButton(UIButton* button) {
    [button release];
    button = [[UIButton alloc] init];
} 

原因:operateOnTheButton 不拥有该按钮,它不应该释放该按钮。如果我给你一个按钮,然后在呼叫之后,我有另一个按钮,会发生什么。操作按钮不分配/保留/复制参数。它无权释放它。它可能会给您带来一些问题:

1/ EXEC_BAD_ACCESS:当您在不拥有该对象的方法中释放时,有人可能仍会使用它

2/内存仍然泄漏:释放并不意味着你立即删除内存。你只需将retainCount减1。这意味着如果传递按钮对象的retainCount为2,释放它只会使你的retainCount为1,不会删除对象的内存

3/您的按钮对象也泄漏:没有人可以确保您的按钮对象被释放。你拥有它,所以你必须自己释放它,否则,按钮对象是泄漏的

于 2010-07-23T02:48:40.237 回答
1
  1. 这种情况是正常的,但有些人(包括我自己)更喜欢在同一行自动释放,alloc以避免意外忘记释放。

  2. init通常不会调用alloc。在对象上创建时,alloc首先调用,然后init在新分配的实例上调用。

  3. 这个场景非常正确。然而,保留不会复制。

  4. 在 Objective-C 中,对象总是由它们的指针来引用,这与 C++ 不同。如果您正在编写一个返回字符串的方法,那么您将返回一个NSString*,而不是NSString结构本身。

以下代码片段实际上会导致内存泄漏:

-(void) operateOnTheButton:(UIButton*)button {
    [button release];
    button = [[UIButton alloc] init];
} 

button是一个局部变量,当它超出范围时它会泄漏,因为它还没有被释放。我不太确定这种operateOnTheButton:方法应该做什么。

于 2010-07-23T02:58:35.017 回答
0

为什么要避免 AutoRelease?这是它应该做的方式......

另外,一些建议:

许多人在 iPhone 上犯的一个错误是在 UIViewController 的 viewDidLoad 方法中分配内存,然后在 dealloc 方法而不是 viewDidUnload 方法中释放它。

始终考虑可以多次加载视图。

于 2010-07-23T02:54:21.413 回答
0

“在类的init函数中,分配一个对象,在类的dealloc函数中,释放对象”</p>

首先:作为类的一部分,没有函数。是的,您可以编写 C 函数。您可以编写对实例进行操作的 C 函数。但是,如果您编写其中的一些 -(foo)doSomethings(甚至是 +(foo)doSomethings),您会得到一个消息处理程序。你通过发送消息来使用它们。

这就是为什么不能从-(id)init 消息中分配实例的原因:init 想与内存和其中的超类实例“对话”。由于您没有分配此空间和功能,因此您将获得一些疯狂的内存位置或(更有可能)为零。

另一方面,你释放对象的方式根本不会释放任何东西。因为 dealloc是在对象被销毁时发送给它的。但是由于您不释放它(因为您想在 dealloc 中执行此操作),因此该对象将永远不会被放置在“天堂之门”的列表中:它保持保留计数为 1(或更大),直到您的应用程序死亡并将分配内存的对象踢回操作系统的空闲池。dealloc-message不是由您发送的。dealloc 仅用于内存管理例程。(我想,如果最超级的 NSObject(您的实例父母和祖父母的根)将收到一个释放并且它看到保留计数应该低于 1,则发送此消息。)

如果您真的想在 Objective C 中进行内存管理,将不得不编写自己的框架;您自己的“符合 C++ 的 Cocoa”。恕我直言。

问候

于 2010-07-24T00:00:28.257 回答