iOS APP随着ARC没有泄漏,但实时字节增加

时间:2014-01-06 13:35:00

标签: ios objective-c sqlite memory-leaks

非常感谢@Hot Licks的帮助,他指出我的sqlite3代码有什么问题。我修改了代码,sqlite3增加的实时字节消失了。

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

我的新问题是:在乐器的每一代之间仍然存在一些增加的实时字节,但似乎所有对象都是由ios SDK的代码构成的,而不是我的代码。 那么我应该单独增加,不需要担心吗?

enter image description here

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

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

2)使主控器嵌入Tab键控制器,并添加一个新选项卡,因此主控制器是一个带有2个选项卡的标签栏控制器。所有这些都是在故事板中完成的。

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

4)将tableview,textview作为outletViewController.h中的插座连接。

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

5)修改主控制器中的“tableview:didSelectRowAtIndexPath”功能:

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

6)修改详细视图控制器中的代码,请在后半部分查看“详细控制器代码的相关片段”

它有问题吗?

非常感谢帮助!!!

较旧的部分:

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

我的问题是:

  1. 我的代码出了什么问题?我想也许每次点击一个项目,都会重新创建细节的界面?
  2. 如何找到泄漏的代码?
  3. 由于一些爱好者的帮助,我将我的问题缩小到从sqlite3获取数据的代码。这些代码有什么问题?
  4. 我创建了一个默认的主 - 详细信息应用程序,我多次单击master中的项目,其实时字节也会增加。那么这是否意味着我不需要担心这个问题?
  5. 我的应用程序是一个主要细节的应用程序,使用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
    

    我读了this article about using Heapshot Analysis to find a leak

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

    1. 在乐器中,单击“标记生成”;
    2. 在我的应用中,单击主视图中的项目。 多次重复这两个步骤。
    3. 我得到results from instruments

      打开一代,I got this

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

      enter image description here

      它指向sqlite3的代码:

      enter image description here

      以下是代码片段:

      -(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;
      }
      

      dataFilePath中的LHBConstant方法是:

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

      我还在仪器中使用“memory-> leak”模板,没有泄漏。

      有人可以帮帮我吗? 非常感谢!

3 个答案:

答案 0 :(得分:1)

return poetry; - 我不相信这样返回会导致@finally子句执行。当然sqlite3_finalize(statement);语句不会被执行。

答案 1 :(得分:0)

到目前为止我已经意识到,如果在视图控制器上有一个私有实例变量并且你弹出它,视图控制器就不会被释放。换句话说,一旦拥有私有实例变量,就不会调用视图控制器的dealloc方法。例如,你有一个UIView * _v和NSTimer * _t作为私有实例变量,如下所示:

@implementation MyViewController{

    UIView *_v;
    NSTimer *_t;
}

我正在做的是在视图层次结构中弹出视图控制器之前使它们为零。我认为ARC仍然能够在需要内存后的某个时候解除分配MyViewController。

答案 2 :(得分:0)

你有一些难以回答的问题。

  

由于一些爱好者的帮助,我将我的问题缩小到从sqlite3获取数据的代码。这些代码有什么问题?

对于Sqlite和调试版本:

  1. 确保NDEBUG未定义
  2. 确保定义DEBUG
  3. 使用SQLITE_DEBUG预处理器
  4. 使用SQLITE_MEMDEBUG预处理器
  5. 否则,SQLite会自动进入“发布”模式(来自sqlite3.c,第14816行):

    #if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
    # define NDEBUG 1
    #endif
    

    此外,来自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_CONFIG_MALLOCsqlite3_config来使用Xcode的内存管理器。看看sqlite3_mem_methods。它可能有助于跟踪。 另外,请确保从SQLite中调用错误消息字符串sqlite3_free

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

    ** [[SQLITE_CONFIG_MEMSTATUS]] <dt>SQLITE_CONFIG_MEMSTATUS</dt>
    ** <dd> ^This option takes single argument of type int, interpreted as a
    ** boolean, which enables or disables the collection of memory allocation
    ** statistics. ^(When memory allocation statistics are disabled, the
    ** following SQLite interfaces become non-operational:
    **   <ul>
    **   <li> [sqlite3_memory_used()]
    **   <li> [sqlite3_memory_highwater()]
    **   <li> [sqlite3_soft_heap_limit64()]
    **   <li> [sqlite3_status()]
    **   </ul>)^
    ** ^Memory allocation statistics are enabled by default unless SQLite is
    ** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory
    ** allocation statistics are disabled by default.
    ** </dd>