iPhone上的sqlite3 - 内存吞噬了吗?在解析大量的xml数据时

时间:2010-08-24 06:47:27

标签: iphone sqlite

想象一个包含5000组数据的xml文件。解析时,我NSLog一个元素,它返回了我5000套数据,但我有一个循环,将数据插入sqlite3数据库。似乎在400+之后停止插入。我环顾四周,发现它吞噬了内存?在泄漏仪器中,负责的库是libsqlite3.dylib而责任框架是sqlite3MemMalloc所以我现在如何解决?下面是我的代码。该方法在NSXMLParser didEndElement方法中。

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{     
//NSLog(@"ended element: %@", elementName);
MedicalBedAppDelegate *appDelegate = (MedicalBedAppDelegate *)[[UIApplication sharedApplication] delegate];

if ([elementName isEqualToString:@"Status"]) {

    //setup some globals
    databaseName = @"MedicalBedDatabase.sql";

    NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDir = [documentPaths objectAtIndex:0];
    databasePath = [documentsDir stringByAppendingPathComponent:databaseName];

    //Method Stackoverflow
    sqlite3 *database;
    NSInteger i;
    NSString *p = [[[NSString alloc]init] autorelease];
    NSString *p2 = [[[NSString alloc]init] autorelease];

    NSString *str = [NSString stringWithFormat:@"INSERT INTO UsageData(Date,Time,NoOfMovementRegistered,BreathingRate,TimeSinceLastMovement,Status,bID) VALUES ('%@','%@','%@','%@','%@','%@','%@')",currentDate,currentTime,currentNoMove,currentNoBreathe,currentTimeSinceLastMovement,currentStatus,currentBedNo];
    const char *sqlStmt = [str UTF8String];
    sqlite3_stmt *cmp_sqlStmt;

    NSString *str1 = [NSString stringWithFormat:@"INSERT INTO XMLEntryID(EntryID,bID) VALUES ('%@','%@')",currentEntryID ,p];
    const char *sqlStmt1 = [str1 UTF8String];
    sqlite3_stmt *cmp_sqlStmt1;
    if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
    for (i = 0; i<([appDelegate.beds count]) ; i++) {
        Bed *aBedInstance = (Bed *)[appDelegate.beds objectAtIndex:i];
        EntryID *aEntryIDInstance = (EntryID *)[appDelegate.entryids objectAtIndex:i];

        p = aBedInstance.bedno;
        p2 = aEntryIDInstance.bedID;

        if ([p intValue] == [currentBedNo intValue]) {
            if ([aEntryIDInstance.bedID intValue] == [currentBedNo intValue])
                    {
                      if ([aEntryIDInstance.entryID intValue] > [currentEntryID intValue] && [aEntryIDInstance.bedID intValue] == [currentBedNo intValue]) {
                          //NSLog(@"xmlEntryID is lower then dBCount");
                      } 

                      else if ([aEntryIDInstance.entryID intValue] == [currentEntryID intValue] && [aEntryIDInstance.bedID intValue] == [currentBedNo intValue])
                      {
                          //This if else if statement is needed because if the dbCount == currentEntryID , it would still consider it 
                          // to be dbCount < currentEntryID
                          //NSLog(@" IT IS EQUAL ");    
                      }
                      else if ([aEntryIDInstance.entryID intValue] < [currentEntryID intValue] && [aEntryIDInstance.bedID intValue] == [currentBedNo intValue] )  {
                          ////NSLog(@"dBCount at selectionScreen = %d",[[appDelegate.dBCount objectAtIndex:0] intValue]);
                          //NSLog(@"currentEntryID at selectionScreen = %d",[currentEntryID intValue]);

                          //NSLog(@"xmlEntryID is higher then dBCount");




                              if(sqlite3_prepare_v2(database, sqlStmt, -1, &cmp_sqlStmt, NULL) == SQLITE_OK)
                              {
                                  int returnValue = sqlite3_prepare_v2(database, sqlStmt, -1, &cmp_sqlStmt, NULL);
                                  ((returnValue ==SQLITE_OK)? NSLog(@"INSERT into USAGEDATA SUCCESS") : NSLog(@"INSERT into USAGEDATA Fail"));
                                  sqlite3_step(cmp_sqlStmt);
                              }
                              sqlite3_finalize(cmp_sqlStmt);
                              sqlite3_close(database);


                              if(sqlite3_prepare_v2(database, sqlStmt1, -1, &cmp_sqlStmt1, NULL) == SQLITE_OK)
                              {
                                  int returnValue = sqlite3_prepare_v2(database, sqlStmt, -1, &cmp_sqlStmt1, NULL);
                                  ((returnValue ==SQLITE_OK) ? NSLog(@"INSERT into XMLEntryID SUCCESS") : NSLog(@"INSERT into XMLEntryID Fail"));
                                  sqlite3_step(cmp_sqlStmt1);
                              }
                              sqlite3_finalize(cmp_sqlStmt1);
                              sqlite3_close(database);

                      }
            } 
        }else if ([p intValue] != [currentBedNo intValue]) {

        }
   }
    }
    NSLog(@"adding currentEntryID: %@", currentEntryID);

    sqlite3_close(database);

}

}

2 个答案:

答案 0 :(得分:2)

你在浪费记忆

NSString *p = [[[NSString alloc]init] autorelease];
NSString *p2 = [[[NSString alloc]init] autorelease]

....
p = aBedInstance.bedno;
p2 = aEntryIDInstance.bedID;

当您所做的只是测试aBedInstance.bedno的值时,无需创建新实例。块{/ p>中的其他地方根本没有使用p2

删除前两行并在for循环

中替换以下内容
for (... ) {

    NSString *p = [aBedInstance.bedno retain]; // thread safety
    NSString *p2 = [aEntryIDInstance.bedID retain]; // FIXME: p2 is not used in the scope of this method

    if (....) {
        ....
    }
    ....
    [p release];
    [p2 release];
}

代码还有其他问题。

if(sqlite3_prepare_v2(database, sqlStmt, -1, &cmp_sqlStmt, NULL) == SQLITE_OK) {
    int returnValue = sqlite3_prepare_v2(database, sqlStmt, -1, &cmp_sqlStmt, NULL);
    ((returnValue ==SQLITE_OK)? NSLog(@"INSERT into USAGEDATA SUCCESS") : NSLog(@"INSERT into USAGEDATA Fail"));

    sqlite3_step(cmp_sqlStmt);
}
sqlite3_finalize(cmp_sqlStmt);
sqlite3_close(database);

if(sqlite3_prepare_v2(database, sqlStmt1, -1, &cmp_sqlStmt1, NULL) == SQLITE_OK) {
    int returnValue = sqlite3_prepare_v2(database, sqlStmt, -1, &cmp_sqlStmt1, NULL);
    ((returnValue ==SQLITE_OK) ? NSLog(@"INSERT into XMLEntryID SUCCESS") : NSLog(@"INSERT into XMLEntryID Fail"));
    sqlite3_step(cmp_sqlStmt1);
}
sqlite3_finalize(cmp_sqlStmt1);
sqlite3_close(database);

您应该为每个语句调用一次prepare。对sqlStmt1的sqlite3函数调用将失败,因为您已经关闭了数据库。

if (SQLITE_OK != sqlite3_prepare_v2(database, sqlStmt, -1, &cmp_sqlStmt, NULL)) {
    NSLog(@"Prepare INSERT into USAGEDATA FAILED");
} else {
    if (SQLITE_DONE != sqlite3_step(cmp_sqlStmt)) {
        NSLog(@"INSERT into USAGEDATA FAILED");
    } else { 
        NSLog(@"INSERT into USAGEDATA SUCCEEDED");
    }
    sqlite3_finalize(cmp_sqlStmt);
}

if (SQLITE_OK != sqlite3_prepare_v2(database, sqlStmt, -1, &cmp_sqlStmt1, NULL)) {
    NSLog(@"Prepare INSERT into XMLEntryID FAILED");
} else {
    if (SQLITE_DONE != sqlite3_step(cmp_sqlStmt1)) {
        NSLog(@"INSERT into XMLEntryID FAILED");
    } else { 
        NSLog(@"INSERT into XMLEntryID SUCCEEDED");
    }
    sqlite3_finalize(cmp_sqlStmt1);
}

sqlite3_close(database);

请注意,通过使用使用参数的静态sql字符串,在for循环之外准备语句然后使用sqlite3_bind_*例程来设置for循环内的值,可以提高整体性能。

来自sqlite documentation

语句对象的生命周期如下:

  1. 使用sqlite3_prepare_v2()或相关函数创建对象。
  2. 使用sqlite3_bind _ *()接口将值绑定到主机参数。
  3. 通过调用sqlite3_step()一次或多次来运行SQL。
  4. 使用sqlite3_reset()重置语句,然后返回步骤2.执行此操作零次或多次。
  5. 使用sqlite3_finalize()销毁对象。

答案 1 :(得分:1)

我解决了它,而不是使用prepare语句,而是使用了exec语句

if (sqlite3_exec(database, [str UTF8String], NULL, NULL, &errorMsg)!=SQLITE_OK) {
                              NSAssert1(0,@"Error updating tables: %s",errorMsg);
                              sqlite3_free(errorMsg);
                          }
                        sqlite3_close(database);