Objective c 将7000行插入SQLite数据库时,应用程序崩溃

Objective c 将7000行插入SQLite数据库时,应用程序崩溃,objective-c,ios,sqlite,insert,Objective C,Ios,Sqlite,Insert,我有一个问题,我想在SQLite数据库中插入7000行,但当我到达第6000行时,我的应用程序崩溃,没有错误 我使用了一个分析器,显然问题在于我的iPad上的RAM使用情况(当应用程序分配435MB的RAM时,它崩溃了) 所以我的问题是如何在插入期间管理内存?(是的,我需要本地拥有这些数据,因为我的应用程序需要在断开连接的模式下工作) 以下是我用于在表中插入行的代码: -(void)MultiInsertFamille:(NSArray *)categories{ sqlite3_stmt *s

我有一个问题,我想在SQLite数据库中插入7000行,但当我到达第6000行时,我的应用程序崩溃,没有错误

我使用了一个分析器,显然问题在于我的iPad上的RAM使用情况(当应用程序分配435MB的RAM时,它崩溃了)

所以我的问题是如何在插入期间管理内存?(是的,我需要本地拥有这些数据,因为我的应用程序需要在断开连接的模式下工作)

以下是我用于在表中插入行的代码:

-(void)MultiInsertFamille:(NSArray *)categories{
sqlite3_stmt *stmt=nil;
sqlite3 *bdd;

NSString *database = [[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:@"myBDD.sqlite"];
if (sqlite3_open([database UTF8String], &bdd) == SQLITE_OK)
{
    sqlite3_exec(bdd, "BEGIN", 0, 0, 0);
    const char *sqlstatement="insert into Famille values(?,?,?,?,?,?,?)";

    if(sqlite3_prepare_v2(bdd,sqlstatement , -1, &stmt, NULL)==SQLITE_OK)
    {
        int hasError= 0;
        int i = 0;
        for(NSString *path in categories)
        {
            i++;
            NSString *nameFile = [[path componentsSeparatedByString: @"."] objectAtIndex:0];
            XMLParserFamille *fa = [[XMLParserFamille alloc] initXMLParserFamille:nameFile parseError:nil];
            FamilleData *fam = fa.famille;
            [fa release];

            if (fam.champ_Recherche)
                sqlite3_bind_text(stmt, 1, [fam.champ_Recherche cStringUsingEncoding:NSUTF8StringEncoding], -1, SQLITE_TRANSIENT);
            else
                sqlite3_bind_text(stmt, 1, "NULL", -1, SQLITE_TRANSIENT);

            NSString *pathImTh = @"";
            for (Attribut *att in fam.Attributs)
            {
                if ([att.name isEqualToString:@"ProductThumbnail"])
                {
                    pathImTh = att.value;
                    break;
                }
            }
            if (pathImTh)
                sqlite3_bind_text(stmt, 2, [pathImTh cStringUsingEncoding:NSUTF8StringEncoding], -1, SQLITE_TRANSIENT);
            else
                sqlite3_bind_text(stmt, 2, "NULL", -1, SQLITE_TRANSIENT);

            if (fam.nom)
                sqlite3_bind_text(stmt, 3, [fam.nom cStringUsingEncoding:NSUTF8StringEncoding], -1, SQLITE_TRANSIENT);
            else
                sqlite3_bind_text(stmt, 3, "NULL", -1, SQLITE_TRANSIENT);

            if (fam.ordre)
                sqlite3_bind_int(stmt, 4, [fam.ordre intValue]);
            else
                sqlite3_bind_int(stmt, 4, 0);

            if (fam.uid)
                sqlite3_bind_text(stmt, 5, [fam.uid cStringUsingEncoding:NSUTF8StringEncoding], -1, SQLITE_TRANSIENT);
            else
                sqlite3_bind_text(stmt, 5, "NULL", -1, SQLITE_TRANSIENT);

            if (fam.uid_Categorie)
                sqlite3_bind_text(stmt, 6, [fam.uid_Categorie cStringUsingEncoding:NSUTF8StringEncoding], -1, SQLITE_TRANSIENT);
            else
                sqlite3_bind_text(stmt, 6, "NULL", -1, SQLITE_TRANSIENT);

            if (fam.xmlErrone)
                sqlite3_bind_int(stmt, 7, [fam.xmlErrone intValue]);
            else
                sqlite3_bind_int(stmt, 7, 0);

            if(sqlite3_step(stmt)==SQLITE_DONE)
            {
                NSLog(@"OK");
            }
            else
            {
                NSLog(@"sqlite3_step error famille: '%s'", sqlite3_errmsg(bdd));
                hasError= 1;
            }
            sqlite3_reset(stmt);
            if (i==5500)
                break;
        }
        //if( hasError == 0 ) {
        sqlite3_exec(bdd, "COMMIT", 0, 0, 0);
        //}
        //else {
        //    sqlite3_exec(bdd, "ROLLBACK", 0, 0, 0);
        //}
    }
}
sqlite3_close(bdd);}

您可以尝试执行较小的事务,例如每1000行开始/提交一次

类似的事情可能会有所帮助:

NSInteger rowsProcessed = 0;
for(NSString *path in categories)
{
  if (rowsProcessed == 0)
  {
     sqlite3_exec(bdd, "BEGIN", 0, 0, 0);
  }
  if (rowsProcessed++ > 1000)
  {
     sqlite3_exec(bdd, "COMMIT", 0, 0, 0);
     rowsProcessed = 0;
  }

  ...
  // your inserts here
  ...
}
if (rowsProcessed != 0) 
{
   sqlite3_exec(bdd, "COMMIT", 0, 0, 0);
}
提交而不提交的变通方法(如果完全删除begin/commit,可能会影响性能,因此sqlite会添加隐式事务):

  • 如果开始时表为空,则可以完全删除begin/commit,并在最后删除表中的所有行,以防出错
  • 如果表不是空的,但在发生错误时仍希望删除所有行,则可以删除begin/commit,并添加一些列,这些列在插入这些行时会保留一些唯一的id(此方法中所有插入都有一个唯一的id),以便以后在发生错误时可以按此id删除所有行

在循环中可以创建许多临时对象(例如字符串处理)

你可以试着把你的东西放进自动释放池块 临时对象将在每个循环结束时释放

for(NSString *path in categories) {
   @autoreleasepool {
      your stuff here ..
   }
}

如果没有显式事务,SQLite将自动将每个命令包装到一个事务中,这将导致糟糕的性能。@CL。感谢您指出这一点,更新了我的答案以解决我不理解您所说的问题“SQLite会自动将每一个命令包装到一个事务中,这会导致糟糕的性能。”那么什么是最好的方法呢?你有没有一个例子,tuto,在我的例子中有一个好的代码?(我是SQLite新手,通常我使用CoreData,但我尝试使用SQLite,因为它太慢了,而且使用这段代码我获得了很多性能,所以如果他们有更好的代码来实现更高的性能,我会接受它;p)。你确定空字段应该有字符串
'NULL'
?我想你应该改为使用。我感觉我的数据库保存在RAM中(因为当我提交RAM时,RAM不是空闲的),所以这会释放数据库的对象吗?