[英]SQLite C function in iOS application results in EXC_BAD_ACCESS
In an iOS application, I am trying to strip diacritics from a SQLite field in a table with 1 million rows. 在一个iOS应用程序中,我试图从具有100万行的表中的SQLite字段中删除变音符号。
EDIT : The original answer I referenced has been updated by its author to reflect the accepted answer below. 编辑 : 我引用的原始答案已经由其作者更新 ,以反映下面接受的答案。 It is the best answer for anyone trying to do the same thing. 对于任何尝试做同样事情的人来说,这都是最好的答案。
I got a great start from an earlier answer by Rob . 我从Rob的较早回答中有了一个很好的开端。 For the purposes of this question, I'm trying to copy a table to another using an INSERT INTO ... SELECT ... FROM ...
statement. 出于这个问题的目的,我试图使用INSERT INTO ... SELECT ... FROM ...
语句将表复制到另一个表。 This works fine until I introduce a C function that strips accents and other diacritics from a field: 直到我引入一个从字段中去除重音符号和其他变音符号的C函数,此方法才能正常工作:
INSERT INTO DestinationTable (MasterField, SubField, IndexID) SELECT unaccented(MasterField), SubField, IndexID FROM SourceTable
With the unaccented()
function introduced, I often get an EXC_BAD_ACCESS which I'll specify below. 引入unaccented unaccented()
函数后,我经常得到一个EXC_BAD_ACCESS,我将在下面指定。 But tantalizingly, the SQLite operation will sometimes complete successfully for all 1 million rows . 但令人着迷的是,SQLite操作有时会成功完成所有100万行 。 When I extend the load to replicate across 5 million rows, the application will always crash. 当我将负载扩展到500万行复制时,应用程序将始终崩溃。
TL;DR TL; DR
Here is my source code, with the point of EXC_BAD_ACCESS commented at the bottom of the first function: 这是我的源代码,在第一个函数的底部注释了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));
}
}
Any observations on what I'm doing wrong? 关于我在做什么错的任何发现吗?
May it be this: 可能是这样的:
char *buf = sqlite3_malloc(sizeof(char) * [string length] + 1);
strcpy(buf, [string UTF8String]);
you are allocating length
Unicode characters, instead of UTF-8 bytes. 您分配的是Unicode字符length
,而不是UTF-8字节。
strcpy
may then try to write invalid memory. 然后, strcpy
可能会尝试写入无效的内存。
I suggest you to let SQLite copy string using: 我建议您使用以下方法让SQLite复制字符串:
sqlite3_result_text(context, [string UTF8String], -1, SQLITE_TRANSIENT);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.