sqlite3 на iPhone - память сожралась? при анализе больших объемов данных XML - PullRequest
2 голосов
/ 24 августа 2010

представьте XML-файл с 5000 наборами данных. При разборе я 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 ]

2 голосов
/ 24 августа 2010

Вы тратите впустую память

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

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

Нет необходимости создавать новый экземпляр, когда все, что вы делаете, это проверяете значение aBedInstance.bedno.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);

Вы должны звонить подготовить только один раз для каждого утверждения.Вызов функции sqlite3 для sqlStmt1 завершится ошибкой, поскольку вы уже закрыли базу данных.

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

Жизнь объекта оператора выглядит примерно так:

  1. Создание объекта с помощью sqlite3_prepare_v2 () или связанной функции.
  2. Привязка значений к параметрам хоста с использованием интерфейсов sqlite3_bind _ * ().
  3. Запуск SQLвызывая sqlite3_step () один или несколько раз.
  4. Сбросьте инструкцию с помощью sqlite3_reset (), затем вернитесь к шагу 2. Сделайте это ноль или более раз.
  5. Уничтожьте объект с помощью sqlite3_finalize ().
1 голос
/ 24 августа 2010

Я решил это, вместо использования оператора 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...