【问题标题】:SQLite C function in iOS application results in EXC_BAD_ACCESSiOS 应用程序中的 SQLite C 函数导致 EXC_BAD_ACCESS
【发布时间】:2013-12-13 03:50:05
【问题描述】:

在一个 iOS 应用程序中,我试图从包含 100 万行的表中的 SQLite 字段中去除变音符号。

编辑:作者的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));
    }
}

对我做错了什么有什么看法吗?

【问题讨论】:

    标签: ios objective-c c sqlite


    【解决方案1】:

    可能是这样的:

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

    您正在分配 length Unicode 字符,而不是 UTF-8 字节。

    strcpy 可能会尝试写入无效内存。

    我建议你让 SQLite 复制字符串使用:

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

    【讨论】:

    • 我不知道这是否是保利问题的根源,但这是对该例程的极好修正!我已经相应地更新了我的原始帖子。
    • 哇,不可思议。它刚刚处理了 5000 万条记录,没有 CPU 或内存峰值。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多