【问题标题】:Objective C Memory Management ConfusionObjective C 内存管理混乱
【发布时间】:2009-05-25 18:32:58
【问题描述】:

我正在阅读memory management 的苹果文档,遇到了一些我不明白的东西。基本上,我不明白为什么不需要通过“getter”方法保留实例变量。我写了这个小程序来看看会发生什么。我以为会发生崩溃,但我显然错过了一些东西。

//  main.m
//  Test
//


#import <Foundation/Foundation.h>
#import "Test.h"

int main(int argc, char *argv[])
{
    NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
    
    //Initialize the test object
    Test *t = [[Test alloc] init];
    
    //Set the value to 5
    [t setMyNum:[NSNumber numberWithInt:5]];
    
    //Save a temp number that points to the original number
    NSNumber *tempNum = [t myNum];
    
    //release old number and retain new
    [t setMyNum:[NSNumber numberWithInt:7]];
    
    //Shouldn't this crash because tempNum is pointing to a deallocated NSNumber???
    NSLog(@"the number is %@",tempNum);
    
    [p drain];
    return 0;
}

tempNum 不是指向一个被释放的对象吗??

感谢所有帮助。

编辑

这是getter和setter方法中的代码

#import "Test.h"


@implementation Test
- (void)setMyNum:(NSNumber *)newNum {
    [newNum retain];
    [myNum release];
    myNum = newNum;
}

-(NSNumber *)myNum {
    return myNum;
}
@end

如您所见,我正在对旧对象调用 release。

编辑

这是建议的,我认为 tempNum 仍然存在的原因是因为它还没有从池中自动释放。但是即使将 [pool drain] 移动到 NSLog 消息之前的右侧,也没有崩溃???很奇怪。

【问题讨论】:

    标签: objective-c memory-management


    【解决方案1】:

    由于您没有显式释放任何对象,因此在允许自动释放池耗尽之前不会释放任何内容。尝试在最后一个 NSLog 调用之前插入 [p drain]。它应该使 NSLog 调用崩溃。

    另外,如果你在 setMyNum: 方法中没有保留 NSNumber,你会发现如果在 tempNum 分配之前添加[p drain] 会崩溃。

    为了澄清最初的问题,调用 getter 方法并不(也不应该)必然暗示调用者想要拥有(即保留)该变量。如果是这样的话,这段代码就会泄露:

    NSLog("Number is %@", [t myNum]);
    

    此外,NSNumber 似乎有一个优化,即对于小数字,它们缓存 NSNumber 对象,保留一个额外的副本,并返回该版本。所以对于小常量,[NSNumber numberWithInt: N] 将返回一个具有 2 个引用计数的对象(可通过[theNumber retainCount] 获得)。要明确查看发生了什么,请在程序中使用更大的常量,NSNumber 将保留一个引用计数为 1 的“新鲜”对象(这也将被自动释放)。

    【讨论】:

    • 但是在我的 setMyNum 中,我明确地调用了保留和释放。调用 release 后旧号码会怎样?
    • release 不会释放对象,它只是从该对象的“保留计数”中减去一个。只有当保留计数达到 0 时才会释放对象。在您的情况下,数字(代表 5 的 NSNumber)在创建后立即具有 1 的保留计数,然后在您的 Test 对象中设置时增加到 2,然后在另一个 NSNumber(代表 7)获得时减回到 1在 Test 对象中设置。
    • 我不认为这是完全正确的:我尝试在 NSLog 调用之前插入一个 [p drain] 期待崩溃,但没有崩溃???一定有别的东西保值??还是一头雾水
    • 好吧,出于探索目的(但从不用于应用程序逻辑),您可以询问对象的保留计数:[someObject retainCount]。在那里,您可以探索它是如何随着越来越多的其他对象控制它而上升和下降的。
    • 好吧,看起来 NSNumber 为小整数缓存了一些 NSNumber 对象(即,它保持它们的类级别并保留它们的额外副本)。尝试 numberWithInt:654321。那会崩溃的!
    【解决方案2】:
    #import "Test.h"
    
    @implementation Test 
    
    - (void)setMyNum:(NSNumber *)newNum
    {      
        [newNum retain];
        [myNum release]; 
        myNum = newNum;
    }
    
    -(NSNumber *)myNum 
    {      
        return myNum; 
    } 
    
    @end  
    

    这里在 setter 方法 [myNum release] 中释放了 mynum,但是我们再次给出了一些新值,即 newnum,因此从 getter 方法中,临时数字获得了直到 [p drain] 之前尚未释放的数字所以不会有任何崩溃。

    【讨论】:

      【解决方案3】:
      #import "Test.h"
      
      @implementation Test 
      
      (void)setMyNum:(NSNumber *)newNum
      {      
      
      [newNum retain];
      [myNum release]; 
      myNum = newNum;
      
      }
      
      (NSNumber *)myNum 
      {      
      
      return myNum; 
      
      } 
      
      @end  
      

      这里是setter方法[myNum release];,它释放了myNum,但是我们再次给出了一些新的值newNum,因此从getter方法中临时编号获得了直到没有被释放的编号[p drain]; 所以不会有任何崩溃。即使下面的代码不会因为有自动释放池但没有自动释放方法而崩溃。

      [t setMyNum:[NSNumber numberWithInt:70]];
      

      因此释放池不会释放该数字。

      【讨论】:

      • 不要发布两次;如果您需要进行更改,请编辑您的答案。请同时标记程序示例并使用标准正字法——即,不要全部大写。
      猜你喜欢
      • 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
      相关资源
      最近更新 更多