如何解决快速将数据转换为UnsafeRawPointer,反之亦然
我一直在开发一个使用Pencil Kit的应用程序,并且试图将画布上的图形存储到sqlite3数据库中。为此,我不得不将绘图(类型:数据)转换为UnsafeRawPointer。但是,转换后,当我尝试通过指针访问(打印)图形时,它返回0字节而不是42字节。我在下面的代码中添加了一些打印语句及其返回的内容,希望对您有所帮助。
// 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应用程序的初学者,并且难以快速理解指针。预先谢谢你。
编辑:保存绘图方法:
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,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 = ?",nil) != sqlITE_OK {
print("Could not create (select) query")
return []
}
// bind
sqlite3_bind_text(statement,Nsstring(string:datetoStringFormat(dateDate: selectedDate)).utf8String,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,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
的要点是,它确保字节的连续表示(如果需要,可进行复制),然后为您提供指向在该块有效期内有效的那些字节的指针。该指针在该块之外无效有效。您不得将其退还或以其他方式使其逃脱。但是在该块中,您可以将其用作void *
。这意味着您必须确保sqlite3不会在该块末尾存储drawingPtr
,这就是为什么必须将withUnsafeBytes
放在整个prepare / finalize序列中,而不仅仅是{{1} }。
通常,您不能将UnsafeRawPointers传递给未分配给自己的东西。当您认为确实存在时,无法保证它们所指向的东西继续存在。对于Data,甚至无法保证它代表一个内存块(例如,数据可以从dispatch_data支持)。您访问数据字节的方式是使用bind_blob
。
您的withUnsafeBytes
函数也有一些错误。首先,您不需要进行NSString转换。这行:
check
可以写为:
sqlite3_bind_text(statement,NSString(string:dateToStringFormat(dateDate: selectedDate)).utf8String,-1,nil)
Swift will automatically convert String to C-string when passed to a C function that takes a char *
.
此代码是完全错误的,并且可能就是为什么您得到零字节的原因:
sqlite3_bind_text(statement,dateToStringFormat(dateDate: selectedDate),nil)
指向Blob的指针不是数据。您不能仅以此方式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)))
。您需要知道多长时间。这是您需要的代码:
load
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。