开发者

Leak caused by retain or something else? (Objective-C)

I'm using Leaks, and I believe I've narrowed down the problem, and I'm sure my inexperience has something to do with it. If you see a failure in my logic (as in, there's obviously a simpler way to do this), please let me know.

In this case we're working with 3 classes, a Row class that describes rows in a database, a Database class that contains getRow, insertRow type functions, and a ViewController class.

inside Row.h:

@interface Row : NSObject {
int rowID;
NSString *FirstName;
NSSt开发者_如何学运维ring *LastName;

}

@property (nonatomic) int rowID;
@property (nonatomic, retain) NSString *FirstName; <--see comments below
@property (nonatomic, retain) NSString *LastName; <--see comments below

@end

Hopefully this one is pretty obvious, creates an object that i'm going to push data I pull from the database into.

inside Database.m

-(Row *) getRow {
NSLog(@"Inside getRow");
Row *holder = [[[Row alloc] init] autorelease];

...All the SQL stuff... (Select * from table where id = 1), etc.


char *first = (char *)sqlite3_column_text(statement, 1);
char *last = (char *)sqlite3_column_text(statement, 2);

holder.rowID = sqlite3_column_int(statement, 0);
holder.FirstName = [NSString stringWithUTF8String:first];
holder.LastName = [NSString stringWithUTF8String:last];

return holder;
}

Also, hopefully, fairly self explanatory. Get a specific row from the database, put the info into a Row object (see Row.h).

inside ViewController.m

-(void) viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];

    dataB = [[Database alloc] init];
    [dataB openDB];

    currentRow = [[dataB getRow] retain]; <--See comments below.

    firstNameLabel.text = currentRow.FirstName;

}

-(IBAction)btnGetLastName:(id)sender{
lastNameLabel.text = currentRow.LastName; <--See comments below.
[currentRow release];
currentRow = [dataB getRow];
}

Basically here the view controller give you the first name, and when you click on a button it displays the last name.

So, I'm leaking, and I've narrowed it down to every time I press btnGetLastName. If I don't retain

currentRow = [[dataB getRow] retain]; 

I crash at

lastNameLabel.text = currentRow.LastName; 

(Message sent to deallocated instance).

I've been banging my head into the wall for a couple days on this and I would really like to move forward from here, so if anyone can offer help, it would be greatly appreciated. I will also say that this has all been a learning experience, so in some cases if you have to ask "Why did you do this?" the answer may be "because I'm dumb". Hopefully the problem here is glaring to someone.


Alright, there are a couple of things that I am seeing here that are incorrect. First of all, you should probably make currentRow a retained property, that way you do not have to retain it and release it yourself. Also, make sure your -dealloc method of your Row class look as follows:

- (void)dealloc {
    self.FirstName = nil;
    self.LastName = nil;
    [super dealloc];
}

Make your currentRow a property like so:

@property (nonatomic, retain) Row * currentRow;

Then, assign it like self.currentRow = aRow. Also note that you will need to set self.currentRow = nil in the dealloc method as well. Your app is most likely crashing because you are not retaining currentRow on the line currentRow = [dataB getRow];.

I would also like to point out that it is bad convention to capitalize the first letter of an instance variable.


In the Row dealloc method are you releasing FirstName and LastName?

This seems like the obvious place in what you described for something to be over-retained. Because you are doing holder.FirstName = [NSString stringWithUTF8String:first]; you are doing a retain using the synthesized setter. If you don't explicitly release in dealloc() then FirstName and LastName will end up hanging around.


currentRow = [[dataB getRow] retain];

is correct. If you did self.currentRow = [dataB getRow], (where currentRow was a retained synthesized property), that would also be correct. When you access member variables directly, you are performing an assign, bypassing the synthesized property setter.


Inside getRow, you don't free the char arrays returned by the sqlite3_ functions. That should be two leaks at least.

The crash if you don't retain the currentRow is the opposite: it gets freed alright, but before you are finished with it, and it must be retained to avoid that, also in btnGetLastName. Be sure to release it in dealloc.


In the first call to getRow you retain the result (as you should). Then when the button is pressed, you release it. Good so far. Then you getRow again, but don't retain it.


There may be an issue with doing a release on an autorelease object. So I would do: Row *holder = [[Row alloc] init];

and I trust you release currentRow in your dealloc method?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜