使用指针将 Objective C 转换为 Kotlin
Converting Objective C with Pointers to Kotlin
从事 Kotlin 多平台项目。需要集成SQLCipher,Android部分我们已经做了,现在我在iOS这边做。
我已经构建了 SQLCipher 并使用 c_interop 进行了集成。
Swift 的 SQLCipher 文档是这样说的:
var rc: Int32
var db: OpaquePointer? = nil
var stmt: OpaquePointer? = nil
let password: String = "correct horse battery staple"
rc = sqlite3_open(":memory:", &db)
rc = sqlite3_key(db, password, Int32(password.utf8CString.count))
我一直在看这个,因为将 Swift 转换为 Kotlin 更容易,但是由于它使用 c_interop 作为 KMM,我猜它使用 SQLCipher 定义的普通 C 代码像这样:
sqlite3 *db;
sqlite3_stmt *stmt;
bool sqlcipher_valid = NO;
if (sqlite3_open([databasePath UTF8String], &db) == SQLITE_OK) {
const char* key = [@"BIGSecret" UTF8String];
sqlite3_key(db, key, (int)strlen(key));
我在 Kotlin 中是这样完成这部分的:
var rc: Int
val db: CValuesRef<CPointerVar<cnames.structs.sqlite3>>? = null
val db2: CValuesRef<cnames.structs.sqlite3>? = null
if(sqlite3_open(databasePath, db) == SQLITE_OK) {
NSLog("Successfully opened connection to database")
}
rc = sqlite3_key(db2, null, 0)
但是我在 XCode 在 sqlite3_open(databasePath, db)
中不断崩溃
它说 线程 1:EXC_BAD_ACCESS(代码=1,地址=0x0)
我假设这些问题是因为我真的不熟悉指针,也不确定如何转换为 Kotlin。
从 SQLCiphers 示例中,他们将一个字符串传递给 sqlite3_key,但是当我尝试传递一个字符串时,它说 类型不匹配:推断类型是 String 但 CValuesRef<*>?
我为 sqlite3_open 声明的第一个数据库与 sqlite3_key 的类型不同,但是他们的示例显示他们使用相同的数据库,所以真的不确定如何进行这个.
完整的 SQLCipher 实现是 here
我想通了,希望对您有所帮助。
首先,我必须在 memscoped 中执行所有操作,因此我们可以访问 alloc() 方法。
然后我意识到一个问题是我的数据库路径不正确。
使用 XCode,您可以单击 Window -> 设备和模拟器,单击您的应用程序,然后单击齿轮并按下载容器。然后检查应用程序的容器以查看数据库的位置。对我来说,它在 Application Support.
里面
然后您可以像这样检查您的数据库是否存在:
val fileManager = NSFileManager.defaultManager()
val doesDBExist = fileManager.fileExistsAtPath(databasePath)
然后像这样定义一个指针:
val db: CPointerVar<sqlite3> = allocPointerTo()
sqlite3_open 如果没有找到,实际上会创建一个新的数据库,所以我使用 v2 方法来只是检查它是否存在。请注意,我们使用 .ptr 来引用传入的指针。
val openResult = sqlite3_open_v2(databasePath, db.ptr, SQLITE_OPEN_READWRITE, null)
然后如果你想检查你的密钥,你可以这样做(用你的密钥替换 null):
val keyResult = sqlite3_key(db.value, null, 0)
这应该会给您结果 1,这是一个错误。如 SQLCipher 文档所述,您需要执行进一步的步骤。
val result = sqlite3_exec(db.value, "SELECT count(*) FROM sqlite_master;", null, null, null)
这将尝试在您的数据库上执行命令,如果您的数据库未加密,那么 return 使用 null 应该没问题。如果您输入密码,它应该能够查询数据库。
然后我们需要关闭数据库
sqlite3_close(db.value)
所以当我想检查我的数据库是否加密时,对我来说完整的方法是这样的:
override fun isDatabaseEncrypted(databasePath: String): Boolean {
memScoped {
val fileManager = NSFileManager.defaultManager()
val doesDBExist = fileManager.fileExistsAtPath(databasePath)
NSLog("Result is $doesDBExist")
val db: CPointerVar<sqlite3> = allocPointerTo()
val openResult = sqlite3_open_v2(databasePath, db.ptr, SQLITE_OPEN_READWRITE, null)
NSLog("openResult is $openResult")
val keyResult = sqlite3_key(db.value, null, 0)
NSLog("keyResult is $keyResult")
val result = sqlite3_exec(db.value, "SELECT count(*) FROM sqlite_master;", null, null, null)
sqlite3_close(db.value)
NSLog("Result is $result")
return result != SQLITE_OK
}
}
我知道这比我原来的问题更详细,但希望它能为任何与我有类似问题的人更好地解释事情。
从事 Kotlin 多平台项目。需要集成SQLCipher,Android部分我们已经做了,现在我在iOS这边做。
我已经构建了 SQLCipher 并使用 c_interop 进行了集成。
Swift 的 SQLCipher 文档是这样说的:
var rc: Int32
var db: OpaquePointer? = nil
var stmt: OpaquePointer? = nil
let password: String = "correct horse battery staple"
rc = sqlite3_open(":memory:", &db)
rc = sqlite3_key(db, password, Int32(password.utf8CString.count))
我一直在看这个,因为将 Swift 转换为 Kotlin 更容易,但是由于它使用 c_interop 作为 KMM,我猜它使用 SQLCipher 定义的普通 C 代码像这样:
sqlite3 *db;
sqlite3_stmt *stmt;
bool sqlcipher_valid = NO;
if (sqlite3_open([databasePath UTF8String], &db) == SQLITE_OK) {
const char* key = [@"BIGSecret" UTF8String];
sqlite3_key(db, key, (int)strlen(key));
我在 Kotlin 中是这样完成这部分的:
var rc: Int
val db: CValuesRef<CPointerVar<cnames.structs.sqlite3>>? = null
val db2: CValuesRef<cnames.structs.sqlite3>? = null
if(sqlite3_open(databasePath, db) == SQLITE_OK) {
NSLog("Successfully opened connection to database")
}
rc = sqlite3_key(db2, null, 0)
但是我在 XCode 在 sqlite3_open(databasePath, db)
中不断崩溃它说 线程 1:EXC_BAD_ACCESS(代码=1,地址=0x0)
我假设这些问题是因为我真的不熟悉指针,也不确定如何转换为 Kotlin。
从 SQLCiphers 示例中,他们将一个字符串传递给 sqlite3_key,但是当我尝试传递一个字符串时,它说 类型不匹配:推断类型是 String 但 CValuesRef<*>?
我为 sqlite3_open 声明的第一个数据库与 sqlite3_key 的类型不同,但是他们的示例显示他们使用相同的数据库,所以真的不确定如何进行这个.
完整的 SQLCipher 实现是 here
我想通了,希望对您有所帮助。 首先,我必须在 memscoped 中执行所有操作,因此我们可以访问 alloc() 方法。
然后我意识到一个问题是我的数据库路径不正确。 使用 XCode,您可以单击 Window -> 设备和模拟器,单击您的应用程序,然后单击齿轮并按下载容器。然后检查应用程序的容器以查看数据库的位置。对我来说,它在 Application Support.
里面然后您可以像这样检查您的数据库是否存在:
val fileManager = NSFileManager.defaultManager()
val doesDBExist = fileManager.fileExistsAtPath(databasePath)
然后像这样定义一个指针:
val db: CPointerVar<sqlite3> = allocPointerTo()
sqlite3_open 如果没有找到,实际上会创建一个新的数据库,所以我使用 v2 方法来只是检查它是否存在。请注意,我们使用 .ptr 来引用传入的指针。
val openResult = sqlite3_open_v2(databasePath, db.ptr, SQLITE_OPEN_READWRITE, null)
然后如果你想检查你的密钥,你可以这样做(用你的密钥替换 null):
val keyResult = sqlite3_key(db.value, null, 0)
这应该会给您结果 1,这是一个错误。如 SQLCipher 文档所述,您需要执行进一步的步骤。
val result = sqlite3_exec(db.value, "SELECT count(*) FROM sqlite_master;", null, null, null)
这将尝试在您的数据库上执行命令,如果您的数据库未加密,那么 return 使用 null 应该没问题。如果您输入密码,它应该能够查询数据库。
然后我们需要关闭数据库
sqlite3_close(db.value)
所以当我想检查我的数据库是否加密时,对我来说完整的方法是这样的:
override fun isDatabaseEncrypted(databasePath: String): Boolean {
memScoped {
val fileManager = NSFileManager.defaultManager()
val doesDBExist = fileManager.fileExistsAtPath(databasePath)
NSLog("Result is $doesDBExist")
val db: CPointerVar<sqlite3> = allocPointerTo()
val openResult = sqlite3_open_v2(databasePath, db.ptr, SQLITE_OPEN_READWRITE, null)
NSLog("openResult is $openResult")
val keyResult = sqlite3_key(db.value, null, 0)
NSLog("keyResult is $keyResult")
val result = sqlite3_exec(db.value, "SELECT count(*) FROM sqlite_master;", null, null, null)
sqlite3_close(db.value)
NSLog("Result is $result")
return result != SQLITE_OK
}
}
我知道这比我原来的问题更详细,但希望它能为任何与我有类似问题的人更好地解释事情。