Reputation: 22698
I have an NSMutableArray defined like this:
@property (nonatomic, retain) NSMutableArray *cList;
I have properly placed in my dealloc the release to cList, and in a selector I retrive some data from the database:
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);
When I run with Instruments it shows me some leaks in this lines:
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];
with leaked objects: NSCFString and MyModel. Why? I have properly released cList object.
Upvotes: 0
Views: 969
Reputation: 49354
This block of code
if (self.cList != nil) {
[self.cList release];
self.cList = nil;
}
self.cList = [[NSMutableArray alloc] init];
Can be replaced entirely by this:
self.cList = [NSMutableArray array];
That will eliminate the first leak. Not sure why you're getting a leak warning on the second block of code, since you are releasing newM
properly.
The code leaks because you are creating an object and taking ownership of it ([[NSMutableArray alloc] init]
) and then you are setting that object as a retain
property, taking ownership of the object yet again. In theory, you could get around this by calling release
twice, but that's silly. [NSMutableArray array]
returns an autoreleased mutable array. By setting it to your retain
property, you take ownership of it once.
Also, one other small point, there is no need to check to see if a your property is nil. If you want to remove a property, just do self.cList = nil;
. The runtime will handle releasing the variable for you; that's one of the great reasons to use @properties.
Upvotes: 3
Reputation: 100622
The call to 'alloc' returns an owning (ie, +1) reference. When you assign it to a 'retain' property, you then increment the retain count, giving +2. Hence when you release it later, it leaks with a retain count of +1. Ditto when you add newM to cList, it gets retained again even though you already own it.
Suggested alterations:
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];
So in both cases you're using autorelease to say that the objects should be released automatically in the future if you don't otherwise retain them — which you do by virtue of the (retain) property and by adding to an NSArray.
Upvotes: 1