1

問題があります。7000行をSQL​​iteデータベースに挿入したいのですが、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);}
4

2 に答える 2

2

たとえば、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 を保持する列を追加することができます (この中のすべての挿入に対して 1 つの一意の ID)メソッド) 後でエラーが発生した場合に、この ID ですべての行を削除できます。
于 2012-10-24T08:07:26.810 に答える
0

ループ内で多数の一時オブジェクトが作成される可能性があります (文字列処理など)。

自動解放プール ブロックに自分のものを入れることを試みることができるので、すべてのループの最後に一時オブジェクトが解放されます。

for(NSString *path in categories) {
   @autoreleasepool {
      your stuff here ..
   }
}
于 2012-10-24T08:35:20.407 に答える