微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

快速将数据转换为UnsafeRawPointer,反之亦然

如何解决快速将数据转换为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 举报,一经查实,本站将立刻删除。