概覽
| 場景 | 誰分配 | 誰釋放 | 安全使用方式 |
|---|---|---|---|
| 基本類型傳參(int、double) | — | — | 值傳遞,無分配 |
字串傳參(FFIType.CString) | NAPI 層 | NAPI 層(呼叫後立即釋放) | 無需操心 |
字串回傳值(callString) | C 庫 | C 庫 | 唯讀,不釋放 |
const char* 結構體欄位 | C 庫 | C 庫 | fromPtr 讀取指針值,不釋放 |
結構體傳參(StructSchema) | 使用者(create) | 使用者自行管理 | create 回傳的 ArrayBuffer 用完後釋放 |
結構體回傳值(StructSchema) | ArkFFI(內部 ArrayBuffer) | 使用者呼叫 releasePtr | 讀取後呼叫 releasePtr,否則 Map 常駐 |
getSymbolPtr / ffi.ptr | — | — | 僅獲取位址,無分配 |
基本類型傳參
int、double、char 等基本類型透過暫存器或堆疊傳遞,不涉及任何記憶體分配。直接傳入即可。
字串傳參
當參數類型為FFIType.CString 時,NAPI 層會用 napi_get_value_string_utf8 取得 C 字串,在堆上分配 char*,呼叫 C 函數後立即 delete[]。使用者無需管理。
字串回傳值
callString 回傳 C 庫內部持有的 const char*,ArkFFI 只讀取其內容(napi_create_string_utf8),不分配也不釋放。指針的生命週期由 C 庫管理。
結構體 fromPtr 中 const char* 欄位同樣回傳指針數值,該指針指向 C 庫記憶體,不要透過 releasePtr 釋放。
結構體傳參
透過StructSchema.create() 建立的 ArrayBuffer 由使用者分配,傳遞給 C 函數後由使用者自行管理其生命週期。
結構體回傳值(releasePtr)
當 dlopen / CFunction 的 returns 為 StructSchema 時,ArkFFI 內部會:
- 呼叫 NAPI 取得
ArrayBuffer - 透過
ffi.ptr取得其資料指針 - 將
ArrayBuffer存入g_structResultsMap 防止 GC - 回傳指針給使用者
fromPtr 讀取後,應呼叫 releasePtr 釋放 Map 中的參照,使 ArrayBuffer 可被 GC。
不呼叫 releasePtr 會怎樣?
g_structResults Map 持續持有 ArrayBuffer 的參照,指針始終有效。Map 條目在呼叫 releasePtr 或程式退出前不會被 GC,但一般不構成問題(條目數等於 struct 回傳呼叫次數)。
釋放錯誤指針會怎樣?
releasePtr(ptr) 在 Map 中按 key 查找。如果 ptr 不是由 ArkFFI struct 回傳分配的指針(例如來自 C 庫的 const char*),Map 找不到該 key,靜默無操作,不會崩潰。
指針類型(ptr、callback)
FFIType.ptr 和 FFIType.callback 傳達的是位址值(number),不涉及記憶體分配。位址由 C 庫管理或由 JSCallback 內部槽位管理。
核心原則
ArkFFI 只管理自己分配的記憶體。C 庫的記憶體由 C 庫管理。
| 記憶體來源 | 管理方 |
|---|---|
napi_get_value_string_utf8 副本 | ArkFFI(自動釋放) |
struct 回傳值內部 ArrayBuffer | ArkFFI(releasePtr 釋放) |
C 庫回傳的 const char* | C 庫 |
使用者 StructSchema.create() 產物 | 使用者 |
dlopen 內部 handle | ArkFFI(close() 時 dlclose) |