【问题标题】:Swift: How to know if an instance needs to be copied?Swift:如何知道一个实例是否需要被复制?
【发布时间】:2020-06-15 08:44:19
【问题描述】:

在以下每个示例中,是否需要复制 blob.0text?你怎么知道的?

设置

import SQLite3
private let static_destructor = unsafeBitCast(0, to: sqlite3_destructor_type.self)
private let transient_destructor = unsafeBitCast(-1, to: sqlite3_destructor_type.self)

示例

  1. bind_blob

    func bind_blob(_ stmt: OpaquePointer, _ blob: (UnsafeRawPointer, Int32)) {
      sqlite3_bind_blob(stmt, 1, blob.0, blob.1, transient_destructor)
    }
    
  2. bind_blobs

    func bind_blobs(_ stmt: OpaquePointer, _ blobs: [(UnsafeRawPointer, Int32)]) {
      for (index, blob) in blobs.enumerated() {
        sqlite3_bind_blob(stmt, Int32(index+1), blob.0, blob.1, transient_destructor)
      }
    }
    
  3. bind_text

    func bind_text(_ stmt: OpaquePointer, _ text: String) {
      sqlite3_bind_text(stmt, 1, text, -1, transient_destructor)
    }
    
  4. bind_texts

    func bind_texts(_ stmt: OpaquePointer, _ texts: [String]) {
      for (index, text) in texts.enumerated() {
        sqlite3_bind_text(stmt, Int32(index+1), text, -1, transient_destructor)
      }
    }
    

也就是说,我应该在任何示例中使用static_destructor 而不是transient_destructor 吗?

相关问题:When to use SQLITE_TRANSIENT vs SQLITE_STATIC?

【问题讨论】:

  • 您可以编辑您的问题以包含实际代码,而不是描述代码?
  • @robmayoff 当然。完毕。 :-) 谢谢。

标签: swift memory-management


【解决方案1】:

在您的bind_textbind_texts 函数中,您需要使用瞬态析构函数。当您将 Swift String 作为 const char * 参数传递给 C 函数时,Swift 不保证指针在 C 函数返回后仍然有效。 Calling Functions With Pointer Parameters 文章是这样说的:

您传递给函数的指针只保证在函数调用期间有效。函数返回后,不要持久化指针并访问它。

在您的bind_blobbind_blobs 函数中,这取决于UnsafeRawPointer 的来源以及您执行SQL 语句的时间。如果您使用任何类型的 Swift withUnsafeWhatever 函数获取指针,则在 withUnsafeWhatever 函数返回后指针无效。例如,withUnsafeBytes(of:_:) documentation 是这样说的:

缓冲区指针参数仅在闭包执行期间有效。

如果绑定 blob,执行语句,然后不再使用绑定,则可以使用静态析构函数。静态析构函数没问题:

let data: Data = ...
data.withUnsafeBytes { rawBuffer in
    if let pointer = rawBuffer.baseAddress {
        bind_blob(statement, (pointer, rawBuffer.count))
        execute(statement)
        // No more use of statement unless the parameter is rebound.
    }
}

但这对于静态析构函数是不行的:

let data: Data = ...
data.withUnsafeBytes { rawBuffer in
    if let pointer = rawBuffer.baseAddress {
        bind_blob(statement, (pointer, rawBuffer.count))
    }
}
execute(statement)

【讨论】:

  • 谢谢!多么美丽的答案。感谢您提供相关文档的链接和引用,以及提供清晰的解释和详细说明代码示例。是的,我正在使用withUnsafeBytes。一个后续问题:我如何知道 SQLite 何时完成了 blob 或字符串?
  • 当您停止使用它所绑定的sqlite3_stmt 时,SQLite 就完成了它。
  • 好的。谢谢你。我认为在第一次调用sqlite3_step() 之后,或者在再次调用sqlite3_bind_*() 之后使用相同参数的不同值(我不确定是否允许),或者之后打电话给sqlite3_clear_bindings()
  • 好吧,是的,如果你用另一个调用sqlite3_bind_* 覆盖绑定,或者用sqlite3_clear_bindings 清除它,那么SQLite 也将使用该值“完成”。
  • 好的。凉爽的。谢谢你。 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多