KMM iOS侧使用c_interop集成SQLCipher的指针代码转换问题
问题根因
- 崩溃直接原因是
sqlite3_open的第二个参数传了空指针:C接口的sqlite3_open要求第二个参数是sqlite3 **类型,用来存放返回的数据库实例指针,你传入null相当于让系统往0地址写数据,直接触发EXC_BAD_ACCESS崩溃 - 类型不匹配问题是你混淆了两层指针:
sqlite3_open接收的是二级指针(指向sqlite3指针的指针),而sqlite3_key要求的第一个参数是一级指针(sqlite3实例本身的指针),官方示例用同一个变量是因为C里&db取的是二级指针,存好后db本身就是一级指针 - 字符串传参报错是因为Kotlin字符串不能直接传给C接口,需要先转为C的UTF-8字符串指针
正确实现代码
import kotlinx.cinterop.* import platform.Foundation.NSLog // 这里替换为你cinterop生成的sqlcipher包名 import cnames.structs.sqlite3 fun openEncryptedDatabase(databasePath: String, password: String) = memScoped { // 分配二级指针空间,用来接收sqlite3_open返回的实例 val dbPtr = alloc<CPointerVar<sqlite3>>() // 打开数据库 val openCode = sqlite3_open(databasePath, dbPtr.ptr) if (openCode == SQLITE_OK) { NSLog("Successfully opened connection to database") // 取出一级指针,用来给sqlite3_key传参 val db = dbPtr.value ?: error("Database pointer is null after open success") // 把Kotlin字符串转为C的UTF8指针 password.cstr.usePinned { pinnedPwd -> val keyCode = sqlite3_key( db = db, pKey = pinnedPwd.addressOf(0), nKey = password.length.toInt() ) // 这里可以加keyCode的判断逻辑 if (keyCode == SQLITE_OK) { NSLog("SQLCipher key set success") } } // 注意用完数据库要调用sqlite3_close(db)释放资源 } else { NSLog("Open database failed, code: $openCode") } }
关键逻辑说明
memScoped块:用来管理C指针的生命周期,块内分配的native内存会在块结束后自动释放,避免内存泄漏alloc<CPointerVar<sqlite3>>():分配一个二级指针对象,对应C代码里sqlite3 *db;的&db取值dbPtr.value:取出二级指针指向的一级sqlite3实例指针,对应C代码里直接用的db变量,刚好匹配sqlite3_key的第一个参数类型password.cstr.usePinned:把Kotlin字符串转为C兼容的UTF-8字节数组,固定内存地址后拿到指针传给sqlite3_key,自动处理类型转换问题- 如果编译提示找不到SQLITE_OK常量,可手动定义
const val SQLITE_OK = 0,这是sqlite标准的成功返回码
内容的提问来源于stack exchange,提问作者Ayohaych
相关产品推荐
相关产品推荐

