xCode - 为什么这个内存泄漏?

时间:2011-04-18 15:18:24

标签: xcode memory-leaks nsmutablearray

我有一个像这样定义的NSMutableArray:

@property (nonatomic, retain)           NSMutableArray          *cList;

我已经将我的dealloc正确放置到cList中,并且在选择器中我从数据库中检索了一些数据:

sqlite3 *database;

if(sqlite3_open([self.filePath UTF8String], &database) == SQLITE_OK) { 

    NSString *sqlStatement = [NSString stringWithFormat:@".....", self.someData];
    sqlite3_stmt *compiledStatement; 

    if (self.cList != nil) {
        [self.cList release];
        self.cList = nil;   
    }
    self.cList = [[NSMutableArray alloc] init]; 

    if(sqlite3_prepare_v2(database, [sqlStatement UTF8String], -1, &compiledStatement, NULL) == SQLITE_OK) { 
        sqlite3_bind_text(compiledStatement, 1, [self.someString UTF8String], -1, SQLITE_TRANSIENT);
        sqlite3_bind_text(compiledStatement, 2, [self.someOtherString UTF8String], -1, SQLITE_TRANSIENT);
        while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
            MyModel *newM = [[MyModel alloc] init]; 
            newM.d  = [NSString stringWithUTF8String:(char *) sqlite3_column_text(compiledStatement, 0)];
            newM.c  = [NSString stringWithUTF8String:(char *) sqlite3_column_text(compiledStatement, 1)];
            newM.i  = [NSString stringWithUTF8String:(char *) sqlite3_column_text(compiledStatement, 2)];
            [self.cList addObject:newM];
            [newM release];
        }
    }
    sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);

当我使用仪器运行时,它向我展示了这方面的一些泄漏:

        self.cList = [[NSMutableArray alloc] init]; 
...
        MyModel *newM = [[MyModel alloc] init]; 
        newM.d  = [NSString stringWithUTF8String:(char *) sqlite3_column_text(compiledStatement, 0)];
        newM.c  = [NSString stringWithUTF8String:(char *) sqlite3_column_text(compiledStatement, 1)];
        newM.i  = [NSString stringWithUTF8String:(char *) sqlite3_column_text(compiledStatement, 2)];
        [self.cList addObject:newM];

泄漏的对象:NSCFString和MyModel。为什么?我已经正确发布了cList对象。

2 个答案:

答案 0 :(得分:3)

这段代码

if (self.cList != nil) {
    [self.cList release];
    self.cList = nil;   
}
self.cList = [[NSMutableArray alloc] init]; 

可完全替换为:

self.cList = [NSMutableArray array];

这将消除第一次泄漏。不确定为什么你在第二段代码上收到泄漏警告,因为你正在正确地发布newM


代码泄漏是因为您正在创建一个对象并取得它的所有权([[NSMutableArray alloc] init]),然后您将该对象设置为retain属性,再次获取该对象的所有权。从理论上讲,你可以通过两次调用release来解决这个问题,但这很愚蠢。 [NSMutableArray array]返回一个自动释放的可变数组。通过将其设置为retain属性,您可以获得一次所有权。

另外,另一个小点,没有必要检查你的财产是否为零。如果要删除属性,只需执行self.cList = nil;。运行时将处理为您释放变量;这是使用@properties的重要原因之一。

答案 1 :(得分:1)

对'alloc'的调用返回一个拥有(即+1)引用。将它分配给'retain'属性时,然后递增保留计数,给出+2。因此,当您稍后释放它时,它会以+1的保留计数泄漏。将newM添加到cList时,它会被再次保留,即使您已经拥有它。

建议更改:

    self.cList = [[[NSMutableArray alloc] init] autorelease];  // or [NSMutableArray array]
    ...
    MyModel *newM = [[[MyModel alloc] init] autorelease]; 
    newM.d  = [NSString stringWithUTF8String:(char *) sqlite3_column_text(compiledStatement, 0)];
    newM.c  = [NSString stringWithUTF8String:(char *) sqlite3_column_text(compiledStatement, 1)];
    newM.i  = [NSString stringWithUTF8String:(char *) sqlite3_column_text(compiledStatement, 2)];
    [self.cList addObject:newM];

因此,在这两种情况下,你都使用自动释放来说明如果你不保留对象,将来会自动释放这些对象 - 你可以通过(retain)属性和添加到NSArray来实现