使用“self.variable = value”设置实例变量两次会导致内存泄漏?

时间:2011-04-14 06:23:55

标签: iphone memory variables sdk memory-leaks

直到昨天我才认为我已经理解了iPhone的内存管理。 那么这是我的问题:

// .h file
     @property(nonatomic, retain) NSMutableDictionary *dicParams;
     @property(nonatomic, retain) NSMutableDictionary *dicReferences;
     @property(nonatomic, retain) FtMonitorHandler *monitorHandler;

// .m file
@synthesize dicParams, dicReferences, monitorHandler;

- (id)init {
    self = [super init];

    if (self) {
        self.dicParams = [[NSMutableDictionary alloc] init];
        self.dicReferences = [[NSMutableDictionary alloc] init];
        self.monitorHandler = [[FtMonitorHandlerService alloc] init];
    }
    return self;
}


- (void)dealloc {
    [monitorHandler release];
    [dicParams release];
    [dicReferences release];
    [super dealloc];
}

如果我在其他地方设置了,例如在viewcontroller的分配之后

self.dicParams = dicValues;

......它会变成泄密

我对使用“self。...”设置实例变量的理解是,当前值将被“释放”,然后设置为“retain”。

我尝试了一些乐器。结果:

-(void)createLeak { 
    self.dicParams = [[NSMutableDictionary alloc] init]; 
    self.dicParams = [[NSMutableDictionary alloc] init]; 
}

-(void)createAnotherLeak { 
    self.dicParams = [[NSMutableDictionary alloc] init]; 
    self.dicParams = nil; 
    self.dicParams = [[NSMutableDictionary alloc] init]; 
}

- (void)createWithoutLeak { 
    if(dicParams != nil) [dicParams release]; 
    self.dicParams = [[NSMutableDictionary alloc] init];
}

我是否遗漏了某些内容,或者这是否应该是这样的行为?

编辑:我尝试实施建议的更改。它工作正常,因为我的变量不是GUI元素。 (UIView,UILabel等)

自动释放将导致内存警告后应用程序崩溃

- (void)loadView {  
    [super loadView];
    // ... here is some other stuff ...  
    self.lblDeparture = [[[UILabel alloc] init] autorelease];  
}  

- (void)viewDidUnload {  
    [super viewDidUnload];  
    // Release any retained subviews of the main view.  
    self.lblDeparture = nil;
}

- (void)dealloc {  
    [lblDeparture release];  
    [super dealloc];  
}  

我不太确定,但我认为以下几行是真正的问题:

CGRect frame = CGRectMake(0, 0, self.view.frame.size.width, INFO_VIEW_HEIGHT);  
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];  

[imageView addSubview:lblDeparture];  
[lblDeparture release];  // is this correct?

[self.view addSubview:imageView];  
[imageView release];  

5 个答案:

答案 0 :(得分:4)

如果您是初始化,则需要自动释放。

-(void)dontCreateAnotherLeak {
    self.dicParams = [[[NSMutableDictionary alloc] init] autorelease];
    self.dicParams = nil;
    self.dicParams = [[[NSMutableDictionary alloc] init] autorelease];
}

更容易的是使用便利存取器。

self.dicParams = [NSMutableDictionary dictionary];

如果你想自己处理这个问题。在@synthesize dictParams之上;你也想创建自己的二传手。

-(void)setDictParams:(NSMutableDictionary*) newDictParams
{
    if (dictParams != newDictParams)
    {
        [dictParams release];
        dictParams = [newDictParams retain];
    }
}
这有点简单。但基本上是编译器使用添加到@property标记的保留修饰符创建的内容

答案 1 :(得分:0)

如果您在retain中设置了一个已指定property的实例变量,则保留计数变为1

现在,当您调用self时,因为“self.variable = value”将retain计数增加1,因此总保留计数变为2.

现在要释放它,你需要将保留计数设置为0.因此你需要释放它两次。

希望这有帮助。

答案 2 :(得分:0)

我不确定我完全理解这个问题,但你的第二部分很容易解释......

  

- (void)createLeak {

self.dicParams = [[NSMutableDictionary alloc] init];

self.dicParams = [[NSMutableDictionary alloc] init];

很明显......

现在但是这个

  

- (void)createAnotherLeak {

self.dicParams = [[NSMutableDictionary alloc] init];

self.dicParams = nil;

self.dicParams = [[NSMutableDictionary alloc] init]; }

不释放第一个分配的self.dicParams,而是通过将其设置为nil然后使用新的重置它来忘记对它的任何引用。设置为nil不等于release。如果您已经使用autorelease创建了第一个,然后将其设置为nil则会有所不同。这应该正常工作。你用你的第三个例子做了很多事情!

现在关于你的初始问题,当你写

时泄漏的是什么
  

self.dicParams = dicValues;

变量self.dicParams应该保留该值,直到您再次释放它为止

答案 3 :(得分:0)

我建议您仔细阅读Apple的内存管理编程指南。 http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html

这一切都在那里解释。

我可以看到你犯的一些明显的错误。

首先,您不应在initdealloc中使用访问者。

所以,这个

- (id)init { 
 self = [super init];

 if (self) {
    self.dicParams = [[NSMutableDictionary alloc] init];
    self.dicReferences = [[NSMutableDictionary alloc] init];
    self.monitorHandler = [[FtMonitorHandlerService alloc] init];
 }
 return self;
} 

应该是

- (id)init { 
 self = [super init];

 if (self) {
    dicParams = [[NSMutableDictionary alloc] init];
    dicReferences = [[NSMutableDictionary alloc] init];
    monitorHandler = [[FtMonitorHandlerService alloc] init];
 }
 return self;
} 

其次,当你设置一个保留属性时,你需要释放你设置它的任何东西。

所以,这个

self.dicParams = [[NSMutableDictionary alloc] init];

应该是

self.dicParams = [[[NSMutableDictionary alloc] init] autorelease];

或者你可以这样做

NSMutableDictionary *newDicParams = [[NSMutableDictionary alloc] init];
self.dicParams = newDicParams;
[newDictParams release];

答案 4 :(得分:0)

@synthesize(readwrite, retain, nonatomic)属性生成的setter看起来像这样:

 - (void) setSomething: (id) newSomething;
 {
      if (something != newSomething) {
          [something release];
          something = [newSomething retain];
      }
 }

实例变量something指向的旧对象将被释放,而新对象将被保留。

你的错误是创造对象。您使用[[NSDictionary alloc] init]创建字典。此字典的保留计数为1.您的setter保留对象,因此新的保留计数为2.当您再次调用setter时,原始字典的保留计数会正确地减少 - 它再次为1。要释放字典,您必须再次释放它。为此,有autorelease。一段时间后,自动释放的对象将被释放。因此,设置属性的正确代码将是

 self.something = [[[NSDictionary alloc] init] autorelease];

甚至更好地使用便利分配器

 self.something = [NSDictionary dictionary];

你真的应该阅读并理解Apple的内存管理指南 - 这一切都在那里解释。

顺便说一下,我在这里讨论了保留计数。考虑它们是可以的,但是你永远不应该向对象询问它的​​保留计数,这个值是无用的,因为它很少你想到的。