2013-11-27 29 views
0

在iOS應用程序中,我試圖從具有100萬行的表中的SQLite字段中去除變音符號。iOS應用程序中的SQLite C函數導致EXC_BAD_ACCESS

編輯:該original answer I referenced has been updated其作者以反映下面的接受的答案。對於任何嘗試做同樣事情的人來說,這是最好的答案。

我從an earlier answer by Rob得到了一個很好的開始。爲了這個問題的目的,我試圖使用INSERT INTO ... SELECT ... FROM ...聲明將表複製到另一個表。直到我介紹一個C函數從外地口音剝離和其他變音符號也能正常工作:

INSERT INTO DestinationTable (MasterField, SubField, IndexID) SELECT unaccented(MasterField), SubField, IndexID FROM SourceTable 

隨着推出unaccented()功能,我經常收到我將在下文指定EXC_BAD_ACCESS。但引人注目的是,SQLite操作有時會成功完成所有100萬行。當我擴展負載以複製500萬行時,應用程序將始終崩潰。

TL; DR

下面是我的源代碼,與EXC_BAD_ACCESS點評論在第一函數的底部:

#import <sqlite3.h> 

sqlite3 *db; 

void unaccented(sqlite3_context *context, int argc, sqlite3_value **argv) 
{ 
    if (argc != 1 || sqlite3_value_type(argv[0]) != SQLITE_TEXT) { 
     sqlite3_result_null(context); 
     return; 
    } 

    @autoreleasepool { 
     NSMutableString *string = [NSMutableString stringWithUTF8String:(const char *)sqlite3_value_text(argv[0])]; 
     CFStringTransform((__bridge CFMutableStringRef)string, NULL, kCFStringTransformStripCombiningMarks, NO); 

     char *buf = sqlite3_malloc(sizeof(char) * [string length] + 1); 
     strcpy(buf, [string UTF8String]); 

     sqlite3_result_text(context, buf, -1, sqlite3_free); 
    } // This is where I usually get "EXC_BAD_ACCESS (code=1, address=...)" 
} 

@implementation MyClass 

- (void)myMethod 
{ 
    NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); 
    NSString *cachesDir = [documentPaths objectAtIndex:0]; 
    NSFileManager *fileManager = [NSFileManager defaultManager]; 
    NSError *error = nil; 

    NSDirectoryEnumerator *directoryEnumerator = [fileManager enumeratorAtPath:[[NSBundle mainBundle] resourcePath]]; 

    NSString *fileName; 
    while (fileName = [directoryEnumerator nextObject]) 
    { 
     if ([fileName hasSuffix:@".sqlite"]) 
     { 
      if (![fileManager fileExistsAtPath: [cachesDir stringByAppendingPathComponent:fileName]] == YES) 
      { 
       if (![fileManager copyItemAtPath:[[NSBundle mainBundle] pathForResource:fileName ofType:@""] toPath:[cachesDir stringByAppendingPathComponent:fileName] error:&error]) 
       { 
        NSLog(@"Error description - %@ \n", [error localizedDescription]); 
        NSLog(@"Error reason - %@", [error localizedFailureReason]); 
       } 
       else 
       { 
        NSLog(@"HAI %@", fileName); 

        [self openDB:[cachesDir stringByAppendingPathComponent:fileName]]; 

        NSString *sqlCommand = @"INSERT INTO DestinationTable (MasterField, SubField, IndexID) SELECT unaccented(MasterField), SubField, IndexID FROM SourceTable"; 

        char *sqlError; 
        if (sqlite3_exec(db, [sqlCommand UTF8String], NULL, NULL, &sqlError) != SQLITE_OK) 
        { 
         NSLog(@"sqlite3_exec INSERT NOT SQLITE_OK with error: %d: %s", sqlite3_errcode(db), sqlite3_errmsg(db)); 
        } 

        [self closeDB]; 

        NSLog(@"KTHXBYE %@", fileName); 
       } 
      } 
     } 
    } 
} 

- (void) openDB: (NSString *)filePath 
{ 
    if (sqlite3_open([filePath UTF8String], &db) != SQLITE_OK) 
    { 
     sqlite3_close(db); 
    } 
    else 
    { 
     [self createUnaccentedFunction]; 
    } 
} 

- (void) closeDB 
{ 
    sqlite3_close(db); 
} 

- (void)createUnaccentedFunction 
{ 
    if (sqlite3_create_function_v2(db, "unaccented", 1, SQLITE_ANY, NULL, &unaccented, NULL, NULL, NULL) != SQLITE_OK) 
    { 
     NSLog(@"%s: sqlite3_create_function_v2 error: %s", __FUNCTION__, sqlite3_errmsg(db)); 
    } 
} 

什麼我做錯了任何意見?

回答

1

可能它是這樣的:

char *buf = sqlite3_malloc(sizeof(char) * [string length] + 1); 
strcpy(buf, [string UTF8String]); 

你是分配的,而不是UTF-8字節length Unicode字符。

strcpy然後可能會嘗試寫入無效的內存。

我建議你使用,讓SQLite的複製串:

sqlite3_result_text(context, [string UTF8String], -1, SQLITE_TRANSIENT); 
+0

我不知道這是保利的問題的根源,但是這是一個很好的修正,常規!我已經更新了我原來的帖子。 – Rob

+0

哇,令人難以置信。它只是攪動了5000萬條記錄,沒有CPU或內存尖峯。謝謝! –