【问题标题】:Memory Leaks in CocoaCocoa 中的内存泄漏
【发布时间】:2010-12-01 05:45:23
【问题描述】:

我正在开发一个 iphone 应用程序,但遇到了一些内存泄漏问题。我读过一些关于垃圾收集的文档,它听起来很简单,但我一定遗漏了一些东西。我有一个 viewController 需要访问一个可能需要不时重新填充的数组。这是我所拥有的简化版本:


//AppDelegate.m
- (NSMutableArray *)getMathFacts {
        //Some database stuff
    NSMutableArray * arr = [[NSMutableArray alloc] init];
    while (sqlite3_step(math_fact_statement) == SQLITE_ROW) {
        [arr addObject:[[NSNumber numberWithInt:sqlite3_column_int(math_fact_statement, 0)] autorelease]];
    }
    return arr;
}

//ViewController.h
@interface ReviewViewController : UIViewController  {
    NSMutableArray *reviewArr;
}
@property (retain, nonatomic) NSMutableArray *reviewArr;

//ViewController.m
- (void)viewDidLoad {
    [super viewDidLoad];
    [self loadMathFacts];
}
- (void)loadMathFacts {
    self.reviewArr = [appDelegate getMathFacts];
}
- (void)loadAllMathFacts {
    self.reviewArr = [appDelegate getAllMathFacts];
}
-(IBAction) getAll {
    [reviewArr release];
    [self loadAllMathFacts]
}

GetAllMathFacts 与 getMathFacts 类似,只是 SQL 语句不同。 当我运行此检查是否泄漏时,它就像一个筛子。这似乎很简单,但我觉得我已经尝试了所有方法,它只是移动了泄漏。

任何建议将不胜感激。 谢谢

【问题讨论】:

  • iPhone 没有垃圾收集器。这部分在 MacOS X 上的 Cocoa 中,还没有进入 iPhone。

标签: iphone memory pointers memory-leaks


【解决方案1】:

从 Xcode 3.2 开始,支持运行 Clang 分析器。为此,请选择 Build->Build & Analyze。这将运行分析器,这确实是查找引用计数问题的绝佳工具。

对于 3.2 之前的 Xcode,这可能会有所帮助:Using Clang Static Analyzer from within XCode

【讨论】:

    【解决方案2】:

    在 getMathFacts 中,你做一个

    NSMutableArray * arr = [[NSMutableArray alloc] init];
    

    该数组归您所有。它的保留计数为 1。稍后当

    - (void)loadMathFacts {
        self.reviewArr = [appDelegate getMathFacts];
    }
    

    reviewArr 现在保留了相同的数组,并且保留计数变为 2。

    当你然后做一个

    -(IBAction) getAll {
        [reviewArr release];
        [self loadAllMathFacts]
    }
    

    在第一个发布语句中,您的数组现在被释放一次,并且保留计数变为 1。在 [self loadAllMathFacts] 中

    - (void)loadAllMathFacts {
        self.reviewArr = [appDelegate getAllMathFacts];
    }
    

    self.reviewArr 将释放数组,然后保留一个新数组。在此版本之后,保留计数降至 0。我在这里看不到泄漏。也许在 -getAllMathFacts 中?

    现在,为了让事情看起来更好一点,我要改变的一件事是:

    - (void)loadMathFacts {
        NSMUtableArray array = [appDelegate getMathFacts];
        self.reviewArr = array;
        [array release];
    }
    - (void)loadAllMathFacts {
        NSMUtableArray array = [appDelegate getAllMathFacts];
        self.reviewArr = array;
        [array release];
    
    }
    -(IBAction) getAll {  // you don't need to release in here
        [self loadAllMathFacts]
    }
    

    另外,您应该重命名您的 get 方法调用以使用“new”而不是“get”,因为约定是如果您返回调用者拥有的东西,它应该在方法名称中有 new 或 copy。

    正如另一位回答者所说,您可以使用自动释放,尽管我宁愿在其他情况下使用自动释放。但是,如果您要使用自动释放来做到这一点,我会这样做:

    //AppDelegate.m
    - (NSMutableArray *)getMathFacts {
            //Some database stuff
        NSMutableArray * arr = [[NSMutableArray alloc] init];
        while (sqlite3_step(math_fact_statement) == SQLITE_ROW) {
            [arr addObject:[[NSNumber numberWithInt:sqlite3_column_int(math_fact_statement, 0)] autorelease]];
        }
        return [arr autorelease];
    }
    

    对 -getAllMathFacts 执行相同操作。您仍然应该将代码更改为更像上面的代码,除了您不必在执行 self.reviewArray 后释放,并且您不必更改方法的名称。您必须记住的是,如果您对它们调用保留然后忘记它们,即使自动释放的对象也会泄漏。 autorelease 的好处是对象会在 runloop 期间保留,足够长的时间可以将其交给其他对象并让他们决定是要保留它还是让它过期。

    我希望我没有错过任何东西。通过我的逻辑,随意戳洞或提出问题。我很容易错过一些东西。

    顺便说一句,当你有 self.reviewArr 时:

    @property (retain, nonatomic) NSMutableArray *reviewArr;
    

    执行以下操作:

    - (void)setReviewArr:(NSMutableArray*)array
    {
      NSMutableArray* oldReviewArr = reviewArr;
      reviewArr = [array retain];
      [oldReviewArr release];
    }
    

    【讨论】:

      【解决方案3】:

      iPhone 操作系统实际上没有垃圾收集。您对保留/释放所做的操作称为引用计数。

      解决你的问题可能是让getMathFacts返回一个自动释放的对象(将return arr;更改为return [arr autorelease];),因为属性reviewArr的定义可能类似于@property (retain) NSArray *reviewArr;,这意味着每个当你调用self.reviewArr = something;时,something被保留,这意味着你在loadMathFactsloadAllMathFacts中设置reviewArr后,reviewArr被保留一次太多了。

      【讨论】:

      • loadMathFacts 和 loadAllMathFacts 似乎返回两个不同的数组,而不是同一个数组,因此保留是在不同的数组上完成的,而不是同一个数组。
      • 好的。我们没有 -[getAllMathFacts] 的代码,但那里的问题(及其解决方案)很可能与 -[getMathFacts] 相同。返回时只需添加一个自动释放调用(即使用'return [arr autorelease];'),一切都应该没问题。
      猜你喜欢
      • 2010-11-20
      • 2017-06-09
      • 1970-01-01
      • 1970-01-01
      • 2010-09-15
      • 2021-05-07
      • 1970-01-01
      • 1970-01-01
      • 2012-07-06
      相关资源
      最近更新 更多