问题
In each of the examples below, does blob.0
or text
need to be copied? How do you know?
Setup
import SQLite3
private let static_destructor = unsafeBitCast(0, to: sqlite3_destructor_type.self)
private let transient_destructor = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
Examples
bind_blob
func bind_blob(_ stmt: OpaquePointer, _ blob: (UnsafeRawPointer, Int32)) { sqlite3_bind_blob(stmt, 1, blob.0, blob.1, transient_destructor) }
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) } }
bind_text
func bind_text(_ stmt: OpaquePointer, _ text: String) { sqlite3_bind_text(stmt, 1, text, -1, transient_destructor) }
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) } }
Ie, should I use static_destructor
instead of transient_destructor
in any of the examples?
Related question: When to use SQLITE_TRANSIENT vs SQLITE_STATIC?
回答1:
In your bind_text
and bind_texts
functions, you need to use the transient destructor. When you pass a Swift String
to a C function as a const char *
argument, Swift does not guarantee that the pointer will remain valid after the C function returns. The Calling Functions With Pointer Parameters article says this:
The pointer you pass to the function is only guaranteed to be valid for the duration of the function call. Do not persist the pointer and access it after the function has returned.
In your bind_blob
and bind_blobs
functions, it depends where the UnsafeRawPointer
comes from and when you execute the SQL statement. If you're getting the pointer using any sort of Swift withUnsafeWhatever
function, then the pointer is not valid after the withUnsafeWhatever
function returns. For example, the withUnsafeBytes(of:_:) documentation says this:
The buffer pointer argument is valid only for the duration of the closure’s execution.
If you bind your blob, execute the statement, and then no longer use the binding, then you can use the static destructor. This is okay with the static destructor:
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.
}
}
But this is not okay with the static destructor:
let data: Data = ...
data.withUnsafeBytes { rawBuffer in
if let pointer = rawBuffer.baseAddress {
bind_blob(statement, (pointer, rawBuffer.count))
}
}
execute(statement)
来源:https://stackoverflow.com/questions/60499265/swift-how-to-know-if-an-instance-needs-to-be-copied