iOS应用程序,无泄漏,但活动字节增加

iOS应用程序,无泄漏,但活动字节增加,ios,objective-c,sqlite,memory-leaks,Ios,Objective C,Sqlite,Memory Leaks,多亏@Hot Licks的帮助,他指出了我的sqlite3代码的错误。我修改了代码,增加sqlite3的活动字节消失了 对于其他新的ios开发人员可能也会面临这个问题,我将原始问题留在后面的部分 我的新问题是:在仪器的每一代之间仍然有一些增加的活动字节,但似乎所有的对象都是由ios SDK的代码组成的,而不是我的代码。 那么,我是否应该不去管它,不必担心它 @Hot Clicks说我操作UI的方式可能有问题,所以我详细描述了它: 1) 我在Xcode 5中为ipad创建了一个主细节应用程序 2

多亏@Hot Licks的帮助,他指出了我的sqlite3代码的错误。我修改了代码,增加sqlite3的活动字节消失了

对于其他新的ios开发人员可能也会面临这个问题,我将原始问题留在后面的部分

我的新问题是:在仪器的每一代之间仍然有一些增加的活动字节,但似乎所有的对象都是由ios SDK的代码组成的,而不是我的代码。 那么,我是否应该不去管它,不必担心它

@Hot Clicks说我操作UI的方式可能有问题,所以我详细描述了它:

1) 我在Xcode 5中为ipad创建了一个主细节应用程序

2) 使主控形状嵌入到选项卡栏控制器中,并添加一个新选项卡,这样主控形状就是一个带有两个选项卡的选项卡栏控制器。所有这些都是在故事板中完成的

3) 删除详图控制器中的默认标签。在详细视图中添加一个tableview、一个textview和3个按钮。在tableview中添加原型单元。所有这些都是在故事板中完成的

4) 连接tableview和textview作为detailViewController.h中的出口

@property (strong, nonatomic) IBOutlet UITableView *tableView;
@property (strong, nonatomic) IBOutlet UITextView *explanationText;
5) 修改主控制器中的函数:“tableview:DidSelectRowatineXpath”:

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    LHBPoetry *poetry = [_searchResults objectAtIndex:indexPath.row];
    self.detailViewController.poetryId = poetry.poetryId;
}
6) 修改详细视图控制器中的代码,请在后一部分“详细视图控制器代码的相关片段”中进行检查

有什么问题吗

非常感谢你的帮助

旧的部分:

当我在模拟器中调试我的应用程序时,内存(活动字节)不断增加,但仪器中没有泄漏

我的问题是:

  • 我的代码有什么问题?我想也许每次我点击一个项目,细节的界面都会被重新创建
  • 我怎样才能找到泄漏的代码
  • 由于一些热心者的帮助,我把问题缩小到了从sqlite3获取数据的代码上。这些代码有什么问题
  • 我创建了一个默认的master detail应用程序,多次单击master中的项目,其活动字节也会增加。这是否意味着我不需要担心这个问题
  • 我的应用程序是一个主细节应用程序,使用ARC,SDK是iOS 7,使用Xcode 5编写代码

    此应用程序正在做什么: 在左侧主导航中,有许多项,在右侧详细视图中有一个表。当用户单击某个项目时,详细视图中表格的内容将发生更改

    问题是每次单击主导航中的某个项目时,内存将增加约150K~300K

    代码:

    主控制器代码的相关片段:

    -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
        LHBPoetry *poetry = poetryArray[indexPath.row];
        self.detailViewController.poetryId = poetry.poetryId;
    }
    
    @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
    
    详图控制器代码的相关片段:

    -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
        LHBPoetry *poetry = poetryArray[indexPath.row];
        self.detailViewController.poetryId = poetry.poetryId;
    }
    
    @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
    
    我读了一本书

    在Xcode中,我使用Product->Profile打开仪器,然后选择Memory->Allocations。 然后我会这样做:

  • 在仪器界面,点击“标记生成”
  • 在我的应用程序中,单击主视图中的项目。 重复这两个步骤数次
  • 我知道了

    开一代

    我比较了所有代,发现每次单击主导航中的某个项目时,对象都会增加

    它指向sqlite3的代码:

    以下是代码片段:

    -(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;
    }
    
    LHBConstant
    中的
    dataFilePath
    方法是:

    +(NSString *)dataFilePath{
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentDirectory = [paths objectAtIndex:0];
        documentDirectory = [documentDirectory stringByAppendingString: @"/p140107"];
        return documentDirectory;
    }
    
    我还在仪器中使用“内存->泄漏”模板,没有泄漏

    有人能帮我吗?
    非常感谢

    到目前为止,我已经意识到,如果视图控制器上有一个私有实例变量,并且您将其弹出,则视图控制器不会被解除分配。换句话说,一旦有了私有实例变量,视图控制器的
    dealloc
    方法就不会被调用。例如,您有一个UIView*\u v和一个NSTimer*\u t作为私有实例变量,如下所示:

    @implementation MyViewController{
    
        UIView *_v;
        NSTimer *_t;
    }
    

    我要做的是在从视图层次结构弹出视图控制器之前将它们设为零。我认为ARC在需要内存后仍然能够释放MyViewController

    你有些问题很难回答

    由于一些热心者的帮助,我把问题缩小到了从sqlite3获取数据的代码上。这些代码有什么问题

    对于Sqlite和调试版本:

  • 确保未定义
    NDEBUG
  • 确保定义了
    DEBUG
  • 使用
    SQLITE\u调试
    预处理器
  • 使用
    SQLITE\u MEMDEBUG
    预处理器
  • 否则,SQLite将自动进入“释放”模式(从
    sqlite3.c
    ,第14816行):

    另外,从
    sqlite3.c
    ,第7780行:

    /*
    ** Exactly one of the following macros must be defined in order to
    ** specify which memory allocation subsystem to use.
    **
    **     SQLITE_SYSTEM_MALLOC          // Use normal system malloc()
    **     SQLITE_WIN32_MALLOC           // Use Win32 native heap API
    **     SQLITE_ZERO_MALLOC            // Use a stub allocator that always fails
    **     SQLITE_MEMDEBUG               // Debugging version of system malloc()
    **
    ** On Windows, if the SQLITE_WIN32_MALLOC_VALIDATE macro is defined and the
    ** assert() macro is enabled, each call into the Win32 native heap subsystem
    ** will cause HeapValidate to be called.  If heap validation should fail, an
    ** assertion will be triggered.
    **
    ** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as
    ** the default.
    
    如果需要,您还可以使用
    SQLITE\u CONFIG\u MALLOC
    sqlite3\u CONFIG
    来使用Xcode的内存管理器。看看
    sqlite3\u mem\u方法
    。它可能有助于跟踪。 另外,请确保您正在对来自SQLite的错误消息字符串调用
    sqlite3\u free

    最后,您可以查询SQlite的内存统计信息(从
    sqlite3.c
    ,第1550行):

    **[[SQLITE\u CONFIG\u MEMSTATUS]]SQLITE\u CONFIG\u MEMSTATUS
    **^此选项接受int类型的单个参数,解释为
    **布尔值,用于启用或禁用内存分配集合
    **统计数字^(禁用内存分配统计信息时
    **以下SQLite接口变得不可操作:
    **
      **
    • [sqlite3\u内存使用() **
    • [sqlite3\u memory\u highwater()] **
    • [sqlite3\u soft\u heap\u limit64()] **
    • [sqlite3_status()] **
    )^ **^除非启用SQLite,否则默认情况下将启用内存分配统计信息 **编译时[SQLITE\u DEFAULT\u MEMSTATUS]=0,在这种情况下为内存 **默认情况下禁用分配统计信息。 **
    返回诗歌
    --我不相信这样返回会导致执行
    @finally
    子句。当然还有
    sqlite3_finalize(语句)语句执行