C/ObjC 内存交互
源:Memory Management and Interop - Kotlin/Native
Kotlin/Native 与 C/Objective-C 的内存交互需要特别注意生命周期管理。本文详解安全的跨边界内存管理模式。
内存管理系统概览
三种内存管理机制
Kotlin/Native 项目中可能同时存在三种内存管理机制:
| 系统 | 管理方式 | 使用场景 |
|---|---|---|
| Kotlin GC | 自动垃圾回收 | Kotlin 对象 |
| Objective-C ARC | 自动引用计数 | ObjC 对象 (iOS/macOS) |
| 手动管理 | malloc/free | C 代码分配的内存 |
Kotlin ↔ C 内存边界
Kotlin 分配,C 使用
当 Kotlin 分配内存并传递给 C 代码时,Kotlin 负责内存生命周期:
kotlin
@OptIn(ExperimentalForeignApi::class)
import kotlinx.cinterop.*
// 场景1:临时传递(作用域内)
memScoped {
val buffer = allocArray<ByteVar>(1024)
// C: void process_buffer(uint8_t* data, size_t size)
process_buffer(buffer, 1024u)
// memScoped 结束时自动释放
}
// 场景2:持久化分配
class NativeBuffer(val size: Int) : AutoCloseable {
private val ptr = nativeHeap.allocArray<ByteVar>(size)
fun use(block: (CPointer<ByteVar>) -> Unit) {
block(ptr)
}
override fun close() {
nativeHeap.free(ptr) // ✅ Kotlin 负责释放
}
}
// 使用
NativeBuffer(1024).use { buffer ->
process_buffer(buffer, 1024u)
}c
// C 代码只使用,不释放
void process_buffer(uint8_t* data, size_t size) {
// 使用 data
for (size_t i = 0; i < size; i++) {
data[i] = (uint8_t)i;
}
// ❌ 不要 free(data)
}C 分配,Kotlin 使用
当 C 代码分配内存并传递给 Kotlin 时,需要明确所有权:
kotlin
@OptIn(ExperimentalForeignApi::class)
import platform.posix.*
// C 函数: char* create_string()
fun receiveFromC(): String? {
val cStr = create_string() // C 分配的字符串
if (cStr != null) {
try {
// 转换为 Kotlin String
return cStr.toKString()
} finally {
// ✅ 用 C 的 free 释放
free(cStr)
}
}
return null
}c
#include <stdlib.h>
#include <string.h>
// C 分配内存
char* create_string() {
char* str = (char*)malloc(100);
strcpy(str, "Hello from C");
return str;
}内存所有权原则
谁分配,谁释放:
kotlin
@OptIn(ExperimentalForeignApi::class)
// ✅ 正确:Kotlin 分配 → Kotlin 释放
val kPtr = nativeHeap.allocArray<IntVar>(10)
// ... 使用 ...
nativeHeap.free(kPtr)
// ✅ 正确:C 分配 → C 释放
val cPtr = malloc(40u)?.reinterpret<IntVar>()
// ... 使用 ...
free(cPtr)
// ❌ 错误:混用分配器
val ptr = malloc(40u)?.reinterpret<IntVar>()
nativeHeap.free(ptr) // 崩溃!Objective-C ARC 互操作
ARC 自动管理
Kotlin/Native 与 ObjC 的 ARC 系统无缝集成:
kotlin
@OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)
import platform.Foundation.*
fun useNSObject() {
// ObjC 对象由 ARC 管理
val array = NSMutableArray()
array.addObject("Hello")
array.addObject("World")
// ✅ 无需手动 release
// ARC 自动管理引用计数
}生命周期管理
kotlin
@OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)
class KotlinWrapper {
// Kotlin 持有 ObjC 对象
private val nsData: NSData = "Data".NSData
fun getData(): NSData {
// ✅ 返回时自动 retain
return nsData
}
// ✅ Kotlin GC 回收时,ObjC 对象自动 release
}引用循环问题
混合语言的引用循环无法自动回收:
kotlin
@OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)
import platform.Foundation.*
// ❌ 引用循环:Kotlin ↔ ObjC
class KotlinObject {
var delegate: NSObject? = null // Kotlin → ObjC
}
// ObjC 侧
// @interface MyDelegate : NSObject
// @property (strong) id kotlinRef; // ObjC → Kotlin
// @end
// 循环无法回收!kotlin
@OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)
import platform.Foundation.*
import kotlin.native.ref.*
// ✅ 使用弱引用打破循环
class KotlinObject {
// 使用 WeakReference
private var weakDelegate: WeakReference<NSObject>? = null
fun setDelegate(delegate: NSObject) {
weakDelegate = WeakReference(delegate)
}
fun getDelegate(): NSObject? {
return weakDelegate?.get()
}
}手动引用管理
某些情况需要手动控制 ObjC 对象生命周期:
kotlin
@OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)
import platform.CoreFoundation.*
import platform.Foundation.*
fun manualRetain() {
val array = NSMutableArray()
// 增加引用计数
val cfArray = CFBridgingRetain(array)
// 传递给需要手动管理的 C API
// ...
// 减少引用计数
CFBridgingRelease(cfArray)
}
// Core Foundation 对象
fun useCFString() {
val cfStr = CFStringCreateWithCString(
null,
"Hello".cstr.ptr,
kCFStringEncodingUTF8
)
try {
// 使用 CFString
val length = CFStringGetLength(cfStr)
println("Length: $length")
} finally {
// ✅ 手动释放 Core Foundation 对象
CFRelease(cfStr)
}
}StableRef 跨边界传递
Kotlin 对象传递给 C
StableRef 允许将 Kotlin 对象作为不透明指针传递给 C/ObjC:
kotlin
@OptIn(ExperimentalForeignApi::class)
import kotlin.native.ref.*
class KotlinData(val value: String)
// Kotlin → C
fun passToC(): COpaquePointer {
val data = KotlinData("Important Data")
// 创建稳定引用
val ref = StableRef.create(data)
// 转换为 C 指针
return ref.asCPointer()
}
// C 回调中恢复
val callback = staticCFunction<COpaquePointer?, Unit> { ptr ->
if (ptr != null) {
// 恢复 Kotlin 对象
val data = ptr.asStableRef<KotlinData>().get()
println(data.value)
}
}
// 清理
fun cleanup(ptr: COpaquePointer) {
ptr.asStableRef<KotlinData>().dispose()
}StableRef 生命周期
kotlin
@OptIn(ExperimentalForeignApi::class)
import kotlin.native.ref.*
class ResourceManager {
private val refs = mutableListOf<StableRef<*>>()
fun <T : Any> registerKotlinObject(obj: T): COpaquePointer {
val ref = StableRef.create(obj)
refs.add(ref)
return ref.asCPointer()
}
fun cleanup() {
refs.forEach { it.dispose() }
refs.clear()
}
}内存调试
检测泄漏
kotlin
@OptIn(ExperimentalStdlibApi::class, ExperimentalForeignApi::class)
import kotlin.native.runtime.GC
import kotlin.native.runtime.NativeRuntimeApi
// 监控 Kotlin 内存
fun monitorMemory() {
GC.collect()
val info = GC.lastGCInfo
if (info != null) {
println("Allocated: ${info.allocatedBytes} bytes")
println("Freed: ${info.freedBytes} bytes")
}
}
// Xcode Instruments (iOS/macOS)
// VM Tracker 可以追踪 Kotlin 分配的内存常见问题排查
kotlin
@OptIn(ExperimentalForeignApi::class)
// ❌ 错误
val ptr = nativeHeap.alloc<IntVar>().ptr
nativeHeap.free(ptr)
nativeHeap.free(ptr) // 崩溃!kotlin
@OptIn(ExperimentalForeignApi::class)
// ✅ 正确
var ptr: CPointer<IntVar>? = nativeHeap.alloc<IntVar>().ptr
ptr?.let { nativeHeap.free(it) }
ptr = null最佳实践
实践:RAII 模式
kotlin
@OptIn(ExperimentalForeignApi::class)
import kotlinx.cinterop.*
class NativeResource<T : CVariable>(
private val allocate: () -> CPointer<T>,
private val deallocate: (CPointer<T>) -> Unit
) : AutoCloseable {
private var ptr: CPointer<T>? = allocate()
fun use(): CPointer<T> {
return ptr ?: throw IllegalStateException("Resource已释放")
}
override fun close() {
ptr?.let { deallocate(it) }
ptr = null
}
}
// 使用
NativeResource(
allocate = { nativeHeap.allocArray<ByteVar>(1024) },
deallocate = { nativeHeap.free(it) }
).use { resource ->
val buffer = resource.use()
// 使用 buffer
} // 自动释放实践:明确边界
kotlin
@OptIn(ExperimentalForeignApi::class)
// 封装 C 资源
class CFile(path: String) : AutoCloseable {
private var file: CPointer<FILE>? = fopen(path, "r")
fun read(buffer: CPointer<ByteVar>, size: Int): Int {
val f = file ?: throw IllegalStateException("File closed")
return fread(buffer, 1u, size.toULong(), f).toInt()
}
override fun close() {
file?.let { fclose(it) }
file = null
}
}跨边界内存管理的核心是明确所有权。遵循"谁分配谁释放"原则,使用 RAII 模式能避免绝大多数内存问题。