【问题标题】:Avoid Memory Leak When Property Assigning Twice属性分配两次时避免内存泄漏
【发布时间】:2012-07-05 14:52:02
【问题描述】:

假设我有一个名为 MyTestClass.h 的类。

类结构是这样的

@interface MyTestClass : NSObject {

    NSString *testString;   
}

@property (nonatomic, retain)NSString * testString;

@end

.m 文件

@implementation MyTestClass

@synthesize testString;    

-(id) init{ 

    [self setTestString:@""];           
    return self;
}

-(void)dealloc{ 

    [self.testString release];      
    testString = nil;

    [super dealloc];
}

@end

现在我创建了一个 MyTestClass 对象并分配了两次 testString

MyTestClass * myTestClass = [[MyTestClass alloc] init];

[myTestClass setTestString:@"Hi"];
[myTestClass setTestString:@"Hello"];

现在我想,我的 testStrings 内存泄漏了两次!! (一个通过 init() 另一个通过我的第一个 setTestString 方法)

我说的对吗?还是@property (nonatomic, retain) 会处理/释放先前分配的内存?

或者,在这种情况下,我是否需要像下面的代码一样覆盖 MyTestClass.m 中的 setTestString()

-(void)setTestString:(NSString *)tempString{    

    [testString release];
    testString = nil;

   testString = [tempString retain];
}

感谢您对此问题的任何帮助。

谢谢。

【问题讨论】:

  • 你没有泄露任何我能看到的东西——尽管我使用的是 ARC。据我所知,如果您创建了一个赋值变量,然后在您的方法结束之前没有释放它,您只会在这种情况下泄漏。
  • 请注意,您的dealloc 方法也可以在调用super 之前简化为self.testString = nil;,这将使用自动生成的setter 自动释放之前的testString 值。
  • 苹果文档明确声明不要在 init 和 dealloc 方法中使用访问器。希望引用文档但目前找不到,这将不得不这样做stackoverflow.com/questions/192721/…
  • 你的意思是写“@property (nonatomic, retain)NSString * testString;”和“@synthesize testString;”将自动处理所有内存管理..对吗?如果没有,那么我在哪里释放属性?

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


【解决方案1】:

感谢您对此问题的任何帮助。

我将以此为许可进行一些不一定与您的问题直接相关的观察。

首先,如果您声明一个保留属性(如您所做的那样)并合成它,自动生成的 getter 和 setter 会为您正确处理内存管理。

如果您手动创建 setter(即使@synthesize 存在),您也必须自己进行内存管理。使用任何一个特洛伊木马的例子。

您问题中的设置器包含一个错误,即如果 testString == tempString 即您将属性的值分配给自身,您最终可能会为该属性分配一个悬空指针,因为您有效地释放 tempString 然后保留它.


这是一个你可以安全忽略的实现细节,但是字符串文字,例如@"blah" 被编译成可执行文件,无论释放多少次都不会被释放。因此,对于您的示例,即使设置器没有进行正确的内存管理,也不会有泄漏。


顺便说一下,init 方法的正常模式是

-(id) init
{
    self = [super init];
    if (self != nil)
    {
        // init stuff
    }
    return self;
}

或逻辑等价物。

你应该养成使用它的习惯,因为你需要调用超类的init方法,并且它允许改变self的值,甚至是nil。

此外,虽然通常在释放对象引用后将其设置为 nil 是非常好的做法,但在这两种情况下,都没有必要这样做。第一次,变量即将超出范围,第二次您立即从其他对象分配它。

【讨论】:

    【解决方案2】:

    这不是泄漏。合成变量得到正确处理。

    以这种方式实现合成方法(对于保留关键字)

    @property (nonatomic, retain) NSString *string;
    //backed by variable NSString *_string;
    
    - (void)setString:(NSString*)newString
    {
        if (newString != _string) {
           [_string release];
           _string = [newString retain];
        }
    }
    

    当然这是泄漏:

    - (void)aMethod //of my class with string property
    {
       NSString *aString = [[NSString alloc] initWithString:@"hello"];
       self.string = aString; //retain count of 2
       self.string = @"hello2"; //retain count of 1 for aString
    
      //now I don't release aString.... leak
    
    }
    

    【讨论】:

    • 您是否提到@property(非原子,保留)将处理/释放先前分配的内存。我不想实现 setTestString 方法。我说的对吗?
    • 是的..你不需要实现它。我这样做只是为了向您展示它是如何实现该方法的(以及它是如何在引入属性之前实现的......)......所以......只需编写@property@synthesize这对夫妇就可以了!
    【解决方案3】:

    如果您使用自动生成的设置器(在您的情况下为setTestString:,也由self.testString = ...; 调用),则retain 属性的先前值在设置之前被释放。所以不,您上面发布的代码没有泄漏。

    【讨论】:

      【解决方案4】:

      synthesized setter 方法应该做正确的事。这是它的实现示例:

      - (void)setTestString:(NSString *)tempString
      {    
          [tempString retain];
          [testString release];
          testString = tempString;
      }
      

      或:

      - (void)setTestString:(NSString *)tempString
      {    
          if (tempString != testString)
          {
              [testString release];
              [tempString retain];
              testString = tempString;
          }
      }
      

      【讨论】:

      • 实际使用的是底部的那个。两者都是有效的,但底部的一个给您带来边际性能优势,因为如果 tempString == testString 则不会进行昂贵的保留和发布。
      • 您是否提到@property(非原子,保留)不会处理/释放先前分配的内存。我必须实现 setTestString 方法来管理内存。我说的对吗?
      • @Ramshad 否,使用retain 属性生成的代码类似于我在回答中提到的方法。
      【解决方案5】:

      dealloc 仅在实例被破坏时调用。 如果你这样做:

      [myTestClass setTestString:@"Hi"];
      [myTestClass setTestString:@"Hello"];
      

      在同一个块中,你只是调用了两次 setter。没有内存泄漏。

      【讨论】:

        【解决方案6】:

        当您在指定retain 的属性上使用@synthesize 时,生成的setter 将正确处理多个分配的保留/释放。只要您使用self. 而不是直接转到支持变量并在dealloc 中进行最终发布,就可以了。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-04-08
          • 2013-06-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-03-29
          相关资源
          最近更新 更多