ios:多次使用相同的sqlite参数会导致过早的内存释放

Posted

技术标签:

【中文标题】ios:多次使用相同的sqlite参数会导致过早的内存释放【英文标题】:ios: using the same sqlite parameter more than once causes premature memory deallocation 【发布时间】:2013-01-05 22:37:38 【问题描述】:

注意: 这个问题具有误导性..我认为它使用了多个导致内存错误的参数..但这不是原因..原因是 sql 语句格式不正确..参见the answer下面。

如果创建一个多次使用相同参数的sqlite语句,即

    NSString* updateStmt = @"INSERT INTO search_email(..., subject, ...)"
    " SELECT ..., :subject, ...,"
    " coalesce((SELECT search_email.threadID "
    " FROM search_email "
    " WHERE search_email.subject MATCH :subject2 "
    " ),"
    " :uid"
    " )";

int subjectIndex = sqlite3_bind_parameter_index(searchEmailInsertStmt,":subject");
int subjectIndex2 = sqlite3_bind_parameter_index(searchEmailInsertStmt,":subject2");

...    
sqlite3_bind_text(searchEmailInsertStmt, subjectIndex, [subject UTF8String], -1, SQLITE_TRANSIENT);        // subject
sqlite3_bind_text(searchEmailInsertStmt, subjectIndex2, [subjectCopy UTF8String], -1, SQLITE_TRANSIENT);        // search_email.subject


if (sqlite3_step(searchEmailInsertStmt) != SQLITE_DONE) 
    NSLog(@"Failed step in searchEmailInsertStmt: '%s', '%i'", sqlite3_errmsg([[AddEmailDBAccessor sharedManager] database]), pk);

然后它崩溃并出现以下错误: malloc: *** error for object 0x9b6350: pointer being freed was not allocated *** set a breakpoint in malloc_error_break to debug

知道为什么吗?

更新: 如果我用字符串常量替换主题和主题复制,即@“subject1”和@“主题2”,它工作得很好..但出于某种原因,我尝试以编程方式复制它,即

NSString* subjectCopy = [NSString alloc];
subjectCopy = [subject retain];

这些都不起作用。将 SQLITE_TRANSIENT 更改为 SQLITE_STATIC 也没有效果。

更新 2: 在 malloc_error_break 中断后 bt 的输出:

thread #6: tid = 0x2503, 0x99a20815 libsystem_c.dylib`malloc_error_break, stop reason = breakpoint 1.1
frame #0: 0x99a20815 libsystem_c.dylib`malloc_error_break
frame #1: 0x99a21d51 libsystem_c.dylib`free + 346
frame #2: 0x0005d5e8 reMail`sqlite3MemFree + 40 at sqlite3.c:12272
frame #3: 0x0002a53e reMail`sqlite3_free + 126 at sqlite3.c:15653
frame #4: 0x0004e670 reMail`sqlite3Fts3ExprFree + 64 at sqlite3.c:101490
frame #5: 0x0004e665 reMail`sqlite3Fts3ExprFree + 53 at sqlite3.c:101489
frame #6: 0x0003fbf1 reMail`fulltextClose + 49 at sqlite3.c:97401
frame #7: 0x000b48f3 reMail`sqlite3VdbeFreeCursor + 163 at sqlite3.c:47461
frame #8: 0x000aebb8 reMail`sqlite3VdbeExec + 17576 at sqlite3.c:54042
frame #9: 0x00032273 reMail`sqlite3Step + 467 at sqlite3.c:49459
frame #10: 0x00031f5e reMail`sqlite3_step + 78 at sqlite3.c:49531
frame #11: 0x000ff2ae reMail`-[EmailProcessor insertIntoSearch:withMetaString:withUid:withSubject:withBody:withFrom:withTo:withCc:withFolder:] + 1854 at EmailProcessor.m:934
frame #12: 0x001005a1 reMail`-[EmailProcessor addEmail:] + 3153 at EmailProcessor.m:1015
frame #13: 0x000fd673 reMail`-[EmailProcessor addEmailWrapper:] + 4035 at EmailProcessor.m:651
frame #14: 0x0324c1bd CoreFoundation`__invoking___ + 29
frame #15: 0x0324c0d6 CoreFoundation`-[NSInvocation invoke] + 342
frame #16: 0x017c36b5 Foundation`-[NSInvocationOperation main] + 45
frame #17: 0x01738d23 Foundation`-[__NSOperationInternal start] + 736
frame #18: 0x01738a34 Foundation`-[NSOperation start] + 79
frame #19: 0x017c5301 Foundation`__block_global_6 + 150
frame #20: 0x02ec053f libdispatch.dylib`_dispatch_call_block_and_release + 15
frame #21: 0x02ed2014 libdispatch.dylib`_dispatch_client_callout + 14
frame #22: 0x02ec32e8 libdispatch.dylib`_dispatch_root_queue_drain + 335
frame #23: 0x02ec3450 libdispatch.dylib`_dispatch_worker_thread2 + 39
frame #24: 0x99a09e12 libsystem_c.dylib`_pthread_wqthread + 441

【问题讨论】:

你为什么使用SQLITE_TRANSIENT :1,$s/SQLITE_TRANSIENT/0/g 我认为会这样做。 我不知道大声笑..代码是从做remail电子邮件客户端的人那里复制的..但我很想实现你的建议..最后的事情看起来像这样sqlite3_bind_text(searchEmailInsertStmt, 8, [folder UTF8String], -1, :1,$s/SQLITE_TRANSIENT/0/g);? 您不能将这样的命令作为 sqlite3_bind_text 语句的一部分 对不起;我开玩笑的评论是一个vi 命令,用0 替换所有出现的SQLITE_TRANSIENT。 sqlite 不需要复制绑定值,因为该语句会立即执行。 【参考方案1】:

我终于找到了!在追逐了这么多红鲱鱼之后..我得到的最好的建议是on a forum specialized for sqlite:关键是要让sqlite的胆量远离我的调试范围,这极不可能是sqlite的错。

我基本上决定将我的 sql 语句分解成更小的部分并单独运行:

导致内存问题的原始sql语句:

    NSString* updateStmt = @"INSERT INTO search_email(docid, meta, subject, body, sender, tos, ccs, folder, threadid)"
    " SELECT ?, ?, ?, ?, ?, ?, ?, ?,"
    " coalesce((SELECT search_email.threadID "
    " FROM search_email "
    " WHERE search_email.subject MATCH ?  UNION SELECT * FROM "
    " (SELECT threadID FROM  (SELECT threadID FROM search_email WHERE search_email.sender MATCH ? "
    " INTERSECT SELECT threadID FROM search_email WHERE search_email.tos MATCH ? ) "
    "  UNION "
    " SELECT threadID FROM (SELECT threadID FROM search_email WHERE search_email.sender MATCH ? "
    "       INTERSECT SELECT threadID FROM search_email WHERE search_email.tos MATCH ?)) "
    " LIMIT 1"
    " ),"
    " ?"
    " )";

每当我提供带有特殊字符的字符串来匹配时,就会出现问题。 以下是发送到 MATCH 的有问题的参数示例:

萨巴霍 :) 2 月 1 日的新核心音频问题 - 堆栈内存溢出 参考;伊利诺伊州芝加哥的数据中心测试/ETL 测试员机会。

所以我只是简单地将 MATCH 替换为普通的 = 比较.. 但首先使用正则表达式清理参数:

NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"re:(\\s)*"
                                                                       options:NSRegularExpressionCaseInsensitive
                                                                         error:&error];


                            error:&error];

NSString *filteredSubjectFromRe = [regex stringByReplacingMatchesInString:subject
                                                                  options:0
                                                                    range:NSMakeRange(0, [subject length])
                                                             withTemplate:@""];

if(searchEmailInsertStmt == nil) 

    NSString* updateStmt = @"INSERT INTO search_email(docid, meta, subject, body, sender, tos, ccs, folder, threadid)"
    " SELECT ?, ?, ?, ?, ?, ?, ?, ?,"
    " coalesce((SELECT search_email.threadID "
    " FROM search_email "
    " WHERE search_email.subject = ? UNION SELECT * FROM "
    " (SELECT threadID FROM  (SELECT threadID FROM search_email WHERE search_email.sender = ? "
    " INTERSECT SELECT threadID FROM search_email WHERE search_email.tos = ? ) "
    "  UNION "
    " SELECT threadID FROM (SELECT threadID FROM search_email WHERE search_email.sender = ? "
    "       INTERSECT SELECT threadID FROM search_email WHERE search_email.tos = ?)) "
    " LIMIT 1"
    " ),"
    " ?"
    " )";

【讨论】:

以上是关于ios:多次使用相同的sqlite参数会导致过早的内存释放的主要内容,如果未能解决你的问题,请参考以下文章

Celery/Redis 相同的任务被并行执行多次

如何在 CoreData iOS 谓词构建器中多次传递相同的参数?

在 UICollectionView/UITableView 中为滚动视图的偏移设置动画会导致单元格过早消失

Ruby 1.9.3 / Passenger 3.0.9 升级导致上游过早关闭

iOS 启动屏幕过早关闭

TransactionScope 过早完成