gpt4 book ai didi

swift - 使用 Swift 中的 C 回调时的对象复活

转载 作者:行者123 更新时间:2023-11-30 13:52:27 25 4
gpt4 key购买 nike

我正在尝试从 Swift 访问需要使用 C 回调的 C API。

typedef struct {
void * info;
CFAllocatorRetainCallBack retain;
CFAllocatorReleaseCallBack release;
} CFuncContext;

typedef void (* CFuncCallback)(void * info);

CFuncRef CFuncCreate(CFuncCallback callback, CFuncContext * context);

void CFuncCall(CFuncRef cfunc);

void CFuncRelease(CFuncRef cfunc);

CFuncCreate将回调和上下文存储在堆上(使用 malloc),然后调用 retain回调以保留信息指针。 CFuncCall只是出于演示目的而调用回调,并且 CFuncRelease调用release回调,然后释放内存。

<小时/>

在 Swift 代码中,我想使用 info 的专用对象这使弱引用返回到我的主要对象。当主对象为deinit时编辑,CFuncRelease调用还可以清理 C API 内存。

这样,即使 C API 决定从不同的运行循环执行一些延迟回调,它也始终具有有效的 info指针,直到决定调用 release最终完成时回调。只是一些防御性编程:)

info对象具有以下结构:

final class CInfo<T: AnyObject> {
/// This contains the pointer back to the main object.
weak private(set) var object: T?

init(_ object: T) {
self.object = object
}

/// This variable is used to hold a temporary strong
/// `self` reference while retainCount is not 0.
private var context: CInfo<T>?

/// Number of times that `retain` has been called
/// without a balancing `release`.
private var retainCount = 0 {
didSet {
if oldValue == 0 {
context = self
}
if retainCount == 0 {
context = nil
}
}
}

func retain() {
++retainCount
}

func release() {
--retainCount
}
}
<小时/>

我的主要对象SwiftObj使用 C API。

final class SwiftObj {
typealias InfoType = CInfo<SwiftObj>

private lazy var info: InfoType = { InfoType(self) }()

private lazy var cFunc: CFuncRef = {
var context = CFuncContext(
info: &self.info,
retain: { UnsafePointer<InfoType>($0).memory.retain(); return $0 },
release: { UnsafePointer<InfoType>($0).memory.release() }
)

return CFuncCreate(
/* callback: */ cFuncCallback,
/* context: */ &context
)
}()

func call() {
CFuncCall(cFunc)
}

deinit {
CFuncRelease(cFunc)
}

init() {
call()
}
}

func cFuncCallback(info: UnsafeMutablePointer<Void>) {
print("==> callback from C")
}
<小时/>

在我的测试代码中,我首先分配 SwiftObj通过 IBAction。然后,我将引用设置回 nil再次通过第二个 IBAction。

自动清理代码现在应该正确地取消初始化 SwiftObj ,并且从那里应该通知 C API 它应该清理其内存。然后,release回调info应该调用指针,这会导致 info指针的引用计数达到零,并且还释放 info指针。

然而,deinit 理论似乎并不成立。之后SwiftObj的最终引用被删除,当 release 时又添加了另一个引用。调用回调闭包,并在 CInfo.release 期间重新添加另一个引用。方法(使用仪器的分配跟踪器观察到)。行为还取决于时间 - 例如日志语句的数量。

使用像下面这样的最小样本,它会在初始 release 中立即崩溃。根据时间安排,使用这两条消息中的任意一条消息进行回调关闭 - 第一个消息更常见,如果幸运的话,第二个消息可以通过快速空格制表符空格来实现。也许还有更多 - 正如我所说,在我的完整示例中,如果您输入足够的日志语句,有时它会延长 SwiftObj 的最终清理时间。足以让你看到真正的复活发生。

  • EXC_BAD_ACCESS(代码=1,地址=0x0)
  • EXC_BAD_ACCESS(代码=EXC_I386_GPFLT)
<小时/>

最小示例可以在此处下载:

运行,然后单击“开始”按钮,然后单击“停止”按钮。欢迎来到噩梦。

工具的分配 View 也非常有趣。

最佳答案

似乎是一个编译器错误。解决方法是删除 info 实例变量的惰性属性。

https://bugs.swift.org/browse/SR-74

关于swift - 使用 Swift 中的 C 回调时的对象复活,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34099363/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com