在 swift 中将数据转换为 UnsafeRawPointer,反之亦然
Convert Data into UnsafeRawPointer and vice versa in swift
我一直在开发一个使用 Pencil Kit 的应用程序,我正在尝试将 canvas 上的绘图存储到 sqlite3 数据库中。为此,我必须将绘图(类型:数据)转换为 UnsafeRawPointer。但是,转换后,当我尝试通过指针访问(打印)绘图时,它 returns 0 字节而不是 42 字节。我在下面的代码中添加了一些打印语句及其内容 return,希望对您有所帮助。
// Function that converts drawing data to UnsafeRawPointer
func dataToPtr(drawing: Data) -> UnsafeRawPointer {
let nsData = drawing as NSData
print(nsData) // shows 42 bytes
let rawPtr = nsData.bytes
print(rawPtr.load(as: Data.self))// Shows 0 bytes
return rawPtr
}
// Drawing before conversion
print(canvas.drawing) // Prints: 42 bytes
let drawingPtr = dataToPtr(drawing: canvas.drawing)
// Drawing when accessing the pointer
print(drawingPtr.load(as: Data.self)) // shows 0 bytes
我是制作 iOS 应用程序的初学者,很难理解 swift 中的指针。提前谢谢你。
编辑:保存绘图方法:
func save(canvas: Canvas) {
connect()
// prepare
var statement: OpaquePointer!
// update the drawing given the row id
if sqlite3_prepare_v2(database, "UPDATE drawings SET drawing = ? WHERE rowid = ?", -1, &statement, nil) != SQLITE_OK {
print("Could not create (update) query")
}
// bind place holders
print("DRAWING SAVED: \(canvas.drawing)") // shows 42 bytes
let drawingPtr = dataToPtr(drawing: canvas.drawing)
sqlite3_bind_blob(statement, 1, drawingPtr, -1, nil)
sqlite3_bind_int(statement, 2, Int32(canvas.id))
// execute
if sqlite3_step(statement) != SQLITE_DONE {
print("Could not execute update statement")
}
// finalise
sqlite3_finalize(statement)
}
我想使用 .load() 将指针转换为数据的方法:
// Function to check if canvas for a certain date is already in the database, if exists, return canvas
func check(selectedDate: Date) -> [Canvas] {
connect()
var result: [Canvas] = []
// prepare
var statement: OpaquePointer!
if sqlite3_prepare_v2(database, "SELECT rowid, date, drawing FROM drawings WHERE date = ?", -1, &statement, nil) != SQLITE_OK {
print("Could not create (select) query")
return []
}
// bind
sqlite3_bind_text(statement, 1, NSString(string:dateToStringFormat(dateDate: selectedDate)).utf8String, -1, nil)
// executes
while sqlite3_step(statement) == SQLITE_ROW {
// change string date into Date date
let Date_date = stringToDateFormat(stringDate: String(cString: sqlite3_column_text(statement, 1)))
// if canvas is not empty
if sqlite3_column_blob(statement, 2) != nil {
let drawingPtr = sqlite3_column_blob(statement, 2)
result.append(Canvas(id: Int(sqlite3_column_int(statement, 0)), date: Date_date, drawing: drawingPtr!.load(as: Data.self)))
print("DRAWING NOT NIL")
}
else {
let drawing = Data.init()
result.append(Canvas(id: Int(sqlite3_column_int(statement, 0)), date: Date_date, drawing: drawing))
print("DRAWING IS NIL")
}
}
// finalise
sqlite3_finalize(statement)
return result
}
你需要做的是将你的函数体包裹在 withUnsafeBytes
:
func save(canvas: Canvas) {
connect()
let drawingData = canvas.drawing.dataRepresentation()
drawingData.withUnsafeBytes { drawingBuffer in
let drawingPtr = drawingBuffer.baseAddress!
// ... In here you can use drawingPtr, for example:
sqlite3_bind_blob(statement, 1, drawingPtr, Int32(drawingBuffer.count), nil)
// ...
}
}
在 withUnsafeBytes
块内,您不得引用 drawingData
本身。块外,不得引用drawingPtr
.
withUnsafeBytes
的要点是它确保字节的表示是连续的(如果需要则制作副本),然后为您提供指向那些在块的持续时间内有效的字节的指针.该指针在块外不有效。你不能 return 它或让它逃脱。但在块内,您可以将其用作 void *
。这意味着您必须确保 sqlite3 不会存储 drawingPtr
超过此块的末尾,这就是为什么您必须将 withUnsafeBytes
放在整个 prepare/finalize 序列周围,而不仅仅是 bind_blob
声明。
通常,您不能将 UnsafeRawPointers 传递给您自己未分配的对象。当你认为它存在时,他们指向的东西不会继续存在。在数据的情况下,甚至没有承诺它代表单个内存块(例如,数据可能由 dispatch_data 支持)。您访问数据字节的方式是使用 withUnsafeBytes
.
您的 check
函数也有一些错误。首先,您的 NSString 转换是不必要的。这一行:
sqlite3_bind_text(statement, 1, NSString(string:dateToStringFormat(dateDate: selectedDate)).utf8String, -1, nil)
可以写成:
sqlite3_bind_text(statement, 1, dateToStringFormat(dateDate: selectedDate), -1, nil)
Swift will automatically convert String to C-string when passed to a C function that takes a char *
.
此代码完全错误,可能是您得到零字节的原因:
let drawingPtr = sqlite3_column_blob(statement, 2)
result.append(Canvas(id: Int(sqlite3_column_int(statement, 0)), date: Date_date, drawing: drawingPtr!.load(as: Data.self)))
指向 Blob 的指针不是数据。你不能只是 load
这样。你需要知道它有多长。这是您需要的代码:
// Get the pointer
let drawingPtr = sqlite3_column_blob(statement, 2)!
// Get the length
let drawingLength = Int(sqlite3_column_bytes(statement, 2))
// Copy the bytes into a new Data
let drawing = Data(bytes: drawingPtr, count: drawingLength)
// Now construct your Canvas.
result.append(Canvas(id: Int(sqlite3_column_int(statement, 0)), date: Date_date, drawing: drawing))
我一直在开发一个使用 Pencil Kit 的应用程序,我正在尝试将 canvas 上的绘图存储到 sqlite3 数据库中。为此,我必须将绘图(类型:数据)转换为 UnsafeRawPointer。但是,转换后,当我尝试通过指针访问(打印)绘图时,它 returns 0 字节而不是 42 字节。我在下面的代码中添加了一些打印语句及其内容 return,希望对您有所帮助。
// Function that converts drawing data to UnsafeRawPointer
func dataToPtr(drawing: Data) -> UnsafeRawPointer {
let nsData = drawing as NSData
print(nsData) // shows 42 bytes
let rawPtr = nsData.bytes
print(rawPtr.load(as: Data.self))// Shows 0 bytes
return rawPtr
}
// Drawing before conversion
print(canvas.drawing) // Prints: 42 bytes
let drawingPtr = dataToPtr(drawing: canvas.drawing)
// Drawing when accessing the pointer
print(drawingPtr.load(as: Data.self)) // shows 0 bytes
我是制作 iOS 应用程序的初学者,很难理解 swift 中的指针。提前谢谢你。
编辑:保存绘图方法:
func save(canvas: Canvas) {
connect()
// prepare
var statement: OpaquePointer!
// update the drawing given the row id
if sqlite3_prepare_v2(database, "UPDATE drawings SET drawing = ? WHERE rowid = ?", -1, &statement, nil) != SQLITE_OK {
print("Could not create (update) query")
}
// bind place holders
print("DRAWING SAVED: \(canvas.drawing)") // shows 42 bytes
let drawingPtr = dataToPtr(drawing: canvas.drawing)
sqlite3_bind_blob(statement, 1, drawingPtr, -1, nil)
sqlite3_bind_int(statement, 2, Int32(canvas.id))
// execute
if sqlite3_step(statement) != SQLITE_DONE {
print("Could not execute update statement")
}
// finalise
sqlite3_finalize(statement)
}
我想使用 .load() 将指针转换为数据的方法:
// Function to check if canvas for a certain date is already in the database, if exists, return canvas
func check(selectedDate: Date) -> [Canvas] {
connect()
var result: [Canvas] = []
// prepare
var statement: OpaquePointer!
if sqlite3_prepare_v2(database, "SELECT rowid, date, drawing FROM drawings WHERE date = ?", -1, &statement, nil) != SQLITE_OK {
print("Could not create (select) query")
return []
}
// bind
sqlite3_bind_text(statement, 1, NSString(string:dateToStringFormat(dateDate: selectedDate)).utf8String, -1, nil)
// executes
while sqlite3_step(statement) == SQLITE_ROW {
// change string date into Date date
let Date_date = stringToDateFormat(stringDate: String(cString: sqlite3_column_text(statement, 1)))
// if canvas is not empty
if sqlite3_column_blob(statement, 2) != nil {
let drawingPtr = sqlite3_column_blob(statement, 2)
result.append(Canvas(id: Int(sqlite3_column_int(statement, 0)), date: Date_date, drawing: drawingPtr!.load(as: Data.self)))
print("DRAWING NOT NIL")
}
else {
let drawing = Data.init()
result.append(Canvas(id: Int(sqlite3_column_int(statement, 0)), date: Date_date, drawing: drawing))
print("DRAWING IS NIL")
}
}
// finalise
sqlite3_finalize(statement)
return result
}
你需要做的是将你的函数体包裹在 withUnsafeBytes
:
func save(canvas: Canvas) {
connect()
let drawingData = canvas.drawing.dataRepresentation()
drawingData.withUnsafeBytes { drawingBuffer in
let drawingPtr = drawingBuffer.baseAddress!
// ... In here you can use drawingPtr, for example:
sqlite3_bind_blob(statement, 1, drawingPtr, Int32(drawingBuffer.count), nil)
// ...
}
}
在 withUnsafeBytes
块内,您不得引用 drawingData
本身。块外,不得引用drawingPtr
.
withUnsafeBytes
的要点是它确保字节的表示是连续的(如果需要则制作副本),然后为您提供指向那些在块的持续时间内有效的字节的指针.该指针在块外不有效。你不能 return 它或让它逃脱。但在块内,您可以将其用作 void *
。这意味着您必须确保 sqlite3 不会存储 drawingPtr
超过此块的末尾,这就是为什么您必须将 withUnsafeBytes
放在整个 prepare/finalize 序列周围,而不仅仅是 bind_blob
声明。
通常,您不能将 UnsafeRawPointers 传递给您自己未分配的对象。当你认为它存在时,他们指向的东西不会继续存在。在数据的情况下,甚至没有承诺它代表单个内存块(例如,数据可能由 dispatch_data 支持)。您访问数据字节的方式是使用 withUnsafeBytes
.
您的 check
函数也有一些错误。首先,您的 NSString 转换是不必要的。这一行:
sqlite3_bind_text(statement, 1, NSString(string:dateToStringFormat(dateDate: selectedDate)).utf8String, -1, nil)
可以写成:
sqlite3_bind_text(statement, 1, dateToStringFormat(dateDate: selectedDate), -1, nil)
Swift will automatically convert String to C-string when passed to a C function that takes a char *
.
此代码完全错误,可能是您得到零字节的原因:
let drawingPtr = sqlite3_column_blob(statement, 2)
result.append(Canvas(id: Int(sqlite3_column_int(statement, 0)), date: Date_date, drawing: drawingPtr!.load(as: Data.self)))
指向 Blob 的指针不是数据。你不能只是 load
这样。你需要知道它有多长。这是您需要的代码:
// Get the pointer
let drawingPtr = sqlite3_column_blob(statement, 2)!
// Get the length
let drawingLength = Int(sqlite3_column_bytes(statement, 2))
// Copy the bytes into a new Data
let drawing = Data(bytes: drawingPtr, count: drawingLength)
// Now construct your Canvas.
result.append(Canvas(id: Int(sqlite3_column_int(statement, 0)), date: Date_date, drawing: drawing))