Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS APP With ARC no leaking but live bytes increasing

Thanks a lot to @Hot Licks's help, He points out what's wrong with my sqlite3's codes. I modify the codes and the live bytes increasing by sqlite3 are gone.

For other new ios developers may also face this problem, I leave the original questions in the latter part.

My new question is: there still are some increasing live bytes between every generation in instruments, but it seems that all the objects are made of ios SDK's codes, not my codes. So should I leave the increasing alone, and don't need to worry about it?

enter image description here

@Hot Licks says there may be a problem in my way to manipulate the UI, so I describe it in detail:

1) I create a master detail app for ipad in Xcode 5;

2) Make the master embed in Tab bar controller, and add one new tab, so the master is a tab bar controller with 2 tabs. All these things are done in storyboard.

3) Delete the default label in the detail controller. Add a tableview, a textview and 3 buttons in the detail view. Add a prototype cell in the tableview. All these things are done in storyboard.

4) Connect the tableview, the textview as an outlet in detailViewController.h.

@property (strong, nonatomic) IBOutlet UITableView *tableView;
@property (strong, nonatomic) IBOutlet UITextView *explanationText;

5) modify the function: "tableview: didSelectRowAtIndexPath" in the master controller:

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    LHBPoetry *poetry = [_searchResults objectAtIndex:indexPath.row];
    self.detailViewController.poetryId = poetry.poetryId;
}

6) modify the codes in detail view controller, please check it in the latter part, "a related snippet of the detail controller's code"

Is there a problem in it?

Thanks a lot for helping!!!

The older part :

When I debug my app, in simulator, the memory (live bytes) is continuously increasing, but there is no leaking in Instruments.

My questions are:

  1. What is wrong with my code? I guess maybe every time I click an item, the detail's interface is recreated?
  2. How can I find the leaking code?
  3. Because of some enthusiasts' help, I narrow down my problem to the codes about fetching data from sqlite3. What is wrong with these code?
  4. I create a default master-detail app, I click items in master for many times, and its live bytes also increase. So does this mean I don't need to worry about this problem?

My app is a master-detail app, using ARC, SDK is iOS 7, using Xcode 5 to write code.

What is this app doing: In the left master navigation, there are many items, and in the right detail view there is a table. When users click on an item, the contents of the table in the detail view will change.

The problem is every time I click on an item in the master navigation, the memory will increase for about 150K~300K.

Code:

a related snippet of the master controller's code:

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    LHBPoetry *poetry = poetryArray[indexPath.row];
    self.detailViewController.poetryId = poetry.poetryId;
}

a related snippet of the detail controller's code:

@interface LHBDetailViewController (){
    LHBPoetry *poetry;
    NSArray *sentenceArray;
    PoetryDao *poetryDao;
    PoetryService *poetryService;
}

@property (strong, nonatomic) UIPopoverController *masterPopoverController;
- (void)configureView;


@end

@implementation LHBDetailViewController

#pragma mark - Managing the detail item

- (void)setPoetryId:(int)poetryId
{
    if (_poetryId != poetryId) {
        _poetryId = poetryId;

        // Update the view.
        [self configureView];
    }

    if (self.masterPopoverController != nil) {
        [self.masterPopoverController dismissPopoverAnimated:YES];
    }
}

- (void)configureView
{
    // Update the user interface for the detail item.

    if (self.poetryId > 0) {
        poetry = [poetryDao getPoetryById:self.poetryId];
    }else{
        poetry = [poetryDao getPoetryById:1];
    }

    if(poetry != nil){
        //custom title
        if(poetry.dynasty != nil){
            self.title = [NSString stringWithFormat:@"%@  [%@]%@", poetry.name, poetry.dynasty, poetry.author];
        }else{
            self.title = [NSString stringWithFormat:@"%@  %@", poetry.name, poetry.author];
        }

        //refresh sentenceArray
        sentenceArray = [poetryService changeStringToArray:poetry.content withSplitter:[LHBConstant getPoetrySplitter]];

    }else{
        //custom title
        self.title = @"";
    }

    [_tableView reloadData];
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    poetryDao = [[PoetryDao alloc] init];
    poetryService = [[PoetryService alloc] init];

    [self configureView];
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"detailCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    // Configure the cell...

    NSString *sentence = sentenceArray[indexPath.row];
    cell.textLabel.text = sentence;

    return cell;
}


@end

I read an this article about using Heapshot Analysis to find a leak.

In Xcode, I use Product->Profile, to open instruments, then select Memory -> Allocations. Then I do this:

  1. In instruments, Click "Mark Generation";
  2. In my app, click an item in the master view. repeat this 2 steps for several times.

I got results from instruments.

open an generation, I got this.

I compare all the generations, and I find that an object increases every time I click an item in master navigation.

enter image description here

It points to codes of sqlite3:

enter image description here

Here is the snippet of code:

-(LHBPoetry *) getPoetryById:(int) poetryId{
    sqlite3 *database;

    @try{
        //open database
        if(sqlite3_open([[LHBConstant dataFilePath] UTF8String], &database)!=SQLITE_OK){
            sqlite3_close(database);
            NSAssert(0, @"Failed to open database.");
        }

        //find in database
        NSString *query = @"SELECT id,name,author,dynasty, content, explanation, has_license, has_mastered FROM poetry where id = ?";
        sqlite3_stmt *statement;
        if(sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil)==SQLITE_OK){
            //bind parameter
            sqlite3_bind_int(statement, 1, poetryId);

            while (sqlite3_step(statement)==SQLITE_ROW) {
                int primaryId = sqlite3_column_int(statement, 0);
                char *name = (char *)sqlite3_column_text(statement, 1);
                char *author = (char *)sqlite3_column_text(statement, 2);
                char *dynasty = (char *)sqlite3_column_text(statement, 3);
                char *content = (char *)sqlite3_column_text(statement, 4);
                char *explanation = (char *)sqlite3_column_text(statement, 5);
                int hasLicense = sqlite3_column_int(statement, 6);
                int hasMastered = sqlite3_column_int(statement, 7);

                NSString *nameNS = [NSString stringWithUTF8String:name];
                NSString *authorNS = [NSString stringWithUTF8String:author];
                NSString *dynastyNS = dynasty == nil ? NULL : [NSString stringWithUTF8String:dynasty];
                NSString *contentNS = [NSString stringWithUTF8String:content];
                NSString *explanationNS = explanation == nil ? NULL : [NSString stringWithUTF8String:explanation];

                LHBPoetry *poetry = [[LHBPoetry alloc] initWithId:primaryId withName:nameNS withAuthor:authorNS withDynasty:dynastyNS withContent:contentNS withExplanation:explanationNS withLicense:hasLicense withMastered:hasMastered];

                return poetry;
            }
            sqlite3_finalize(statement);
        }else{
            NSLog(@"poetry getPoetryById fail. database is not ready.");
        }
    }
    @catch (NSException *e) {
        NSLog(@"%@", e);
    }
    @finally {
        sqlite3_close(database);
    }

    return nil;
}

and the method dataFilePath in LHBConstant is:

+(NSString *)dataFilePath{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentDirectory = [paths objectAtIndex:0];
    documentDirectory = [documentDirectory stringByAppendingString: @"/p140107"];
    return documentDirectory;
}

I also use "memory->leak" template in instruments, there's no leak.

Anyone could help me out? Thanks a lot!

like image 569
abentotoro Avatar asked Nov 11 '22 15:11

abentotoro


1 Answers

return poetry; -- I'm not convinced that returning like that will cause the @finally clause to execute. And certainly the sqlite3_finalize(statement); statement does not get executed.

like image 174
Hot Licks Avatar answered Nov 15 '22 06:11

Hot Licks