假設我在 Rust 中定義了一個函式,如下所示:
#[no_mangle]
pub unsafe extern "C" fn do_something(
my_value: *mut MyStruct,
some_param: c_uint,
content: *mut *mut u8,
length: *mut c_uint,
capacity: *mut c_uint,
) -> *mut MyStruct {
// Do something and obtain an `ffi_result`...
let heap_data = ffi_result.as_mut_ptr();
// These values are passed as "out" parameters.
*length = ffi_result.len() as c_uint;
*capacity = ffi_result.capacity() as c_uint;
*content = heap_data;
// We intentionally "leak" this data to the heap.
// The caller is responsible for cleaning it up by calling another function.
std::mem::forget(ffi_result);
std::boxed::Box::into_raw(value_of_type_my_struct)
}
它接受一個指向結構的指標、一個簡單的整數引數、幾個out
稍后可用于創建陣列的引數,并回傳一個指向結構的指標。
現在我將 rust 庫編譯為目標的靜態庫aarch64-apple-ios
。我設定了一個 XCode 專案,將靜態庫添加為依賴項,如此處所述,使用“Objective-C Bridging Header”在其中匯入以下頭檔案
#ifndef libmy_project_h
#define libmy_project_h
#include <stdint.h>
struct myStruct;
struct myStruct *do_something(struct myStruct *state, int someParam, char **content, int *length, int *capacity);
#endif
到目前為止,一切似乎都運行良好,我已經成功地將這個程序用于一大堆其他功能。但是在這種特殊情況下,我無法弄清楚如何從 swift 呼叫此函式。我需要從 swift 呼叫該函式并傳遞content
,length
并capacity
作為輸出引數,以便我以后可以使用指標在 Swift 中創建一個陣列,就像這樣。
這是我到目前為止所嘗試的:
var content = UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>(UnsafeMutablePointer(bitPattern: 0))
var length = UnsafeMutablePointer<Int32>(bitPattern: 0)
var capacity = UnsafeMutablePointer<Int32>(bitPattern: 0)
let my_struct = do_something(my_struct, Int32(some_param), content, length, capacity)
let buffer = UnsafeRawBufferPointer(start: content?.pointee, count: Int(length!.pointee))
var data = Array(repeating: UInt8(0), count: Int(length!.pointee))
data.withUnsafeMutableBytes { arrayPtr in
arrayPtr.copyBytes(from: buffer)
}
但是現在當我執行這個快速代碼片段時,我得到一個EXC_BAD_ACCESS
錯誤,我認為這是因為我手動創建的指標不屬于我的應用程式的地址空間。如何創建可以用作out
引數的指標?
PS 作為參考,這里是 C# 中相同的互操作代碼:
[DllImport("my_dll")]
private static extern IntPtr do_something(IntPtr state, uint someParam, out IntPtr content, out uint length, out uint capacity);
可以這樣呼叫:
IntPtr contentPointer;
uint length, capacity;
IntPtr my_struct = do_something(state, myParam, out contentPointer, out length, out capacity);
byte[] rawContent = new byte[length];
Marshal.Copy(contentPointer, rawContent, 0, (int)length);
// Free the data owned by rust with another FFI call:
free_do_something_result(contentPointer, length, capacity);
uj5u.com熱心網友回復:
var length = UnsafeMutablePointer<Int32>(bitPattern: 0)
您需要為您的輸出引數傳遞存盤空間。這是定義一個空指標。當 Rust 嘗試將結果寫入地址 0 時,它會崩潰,因為您無權在那里寫入。
與其創建兩層指標,不如創建一個你想要的型別的值,然后傳遞&
那個值的地址();這將自動添加額外的指標層。
// Create storage
var content: UnsafeMutablePointer<CChar>? // Could be NULL, so Optional
var length: Int32 = 0
var capacity: Int32 = 0
// Pass as references
do_something(&content, &length, &capacity)
// Copy the data
let data = Array(UnsafeRawBufferPointer(start: content, count: Int(length)))
content
這里仍然是一個指標,因為正在更新的東西是一個指標。你不是為內容提供存盤,Rust 是。但是您確實需要為指標提供存盤空間(這就是這樣做的)。
我無法編譯您的代碼,因為它丟失了很多(在這里使用MCVE會更好),所以我無法測驗這是否完全符合您的意思,但它應該很接近。
在您的示例中,您正在泄漏記憶體,但由于您的 C# 呼叫free_do_something_result
(我假設它會清理它),我假設您實際上在 Swift 中做同樣的事情。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/470117.html
上一篇:“回傳”按鈕使視圖混亂