繁体   English   中英

Xcode Swift MacOS 应用程序,C++ 线程和进度条

[英]Xcode Swift MacOS App, C++ threads and progress bar

首先,我是 Stackoverflow 的新手,我要感谢大家提供的所有重要信息!

我在 Linux 和 Win 下对 C/C++ 有一些经验,最近我正在尝试为 MacOS 开发/移植一个应用程序,因为我刚刚购买了新的 MacBook Air M1。

I've create a new MacOS App in Xcode and I'm using some C++ Classes with extern "C" wrapper... From my Swift code (push button pressed), I'm calling a C function that takes an int* as参数是它执行的操作的百分比进度:

void doSomething(int *progress);

我正在考虑在按下按钮时在单独的线程中运行 function,并根据进度变量的值更新我的 Swift UI 上的进度条。

你能帮我么? 什么可能是最好的解决方案? 我应该为 function 或进度条 UI 更新使用单独的线程吗?

如您所见,使用int *效率相当低。 这是因为对该值所做的分配不能触发任何其他代码运行(在 C 中没有等效于didSet观察者)。 因此,您不得不创建一个单独的线程,其目的是定期轮询 int 值,这非常浪费。

要解决此问题,您需要使用回调。 这些在 Swift 中很容易,因为闭包是您可以轻松创建和传递的第一个 class 公民。 在 C 中,没有这样的东西。 最接近的是 function 指针,但它们是 static,并且缺少闭包可以提供的上下文存储。 这意味着您不能将 Swift 实例方法或闭包作为 c function 指针传递。 您需要一个标记为@convention(c)的闭包,它禁止它捕获任何 state(包括方法中的self )。

但是有一个解决方法。 要理解它,首先您应该了解 C 中使用的典型回调模式。

在 C 中,典型的 function 采用 function 指针(作为回调)也有第二个无类型( void * )参数。 这个想法是你可以手动传递你自己的上下文,无论是什么类型,它都会与 function 指针一起存储。 当触发回调时,C function 将调用您的 function 指针,并传入您给它的上下文。 在您的回调 function (作为回调传递的指针)中,您可以访问此void *context ,然后您可以将其转换为适当的类型并访问您需要的任何内容。

此上下文参数有许多名称,例如contextctxuserdatauserinfo (以及带下划线的驼峰变体)等。

如果您将 C function 更新为:

typedef void (*ProgressChangedCallback)(const int newProgress, void *userinfo);

void doSomething(ProgressChangedCallback callback, void *userinfo) {
   // when the progress updates:
   callback(newProgress, userinfo);
}

在 Swift 中,您可以利用此userinfo参数传递您需要实现回调的上下文。 有两种流行的方法来做到这一点:

  1. 您可以使用userinfo参数走私self ,然后在self上调用方法,并完全访问 object 的实例变量。
  2. 您可以使用userinfo参数在适当的 Swift 闭包中走私。 闭包本身可以捕获 state,包括self或您可能需要的任何其他内容。

它看起来像这样:


// ProgressChangedCallback would have type `@convention(c) (Int, UnsafeMutableRawPointer) -> Void`

class MyClass {
    var someInstanceState = 0
    
    func someFunctionThatHasAccessToInstanceState() {
        defer { someInstanceState += 1 }
        print(someInstanceState)
        // 5. This can update your progress bar UI or whatever.
    }
    
    func registerCallbackForDoSomething() {
        let myProgressChangedCallback: ProgressChangedCallback = { newProgress, userInfo in
            // 3. Unpack `userinfo` to get access to our instance again
            let instance = Unmanaged<MyClass>.fromOpaque(userInfo).takeUnretainedValue()
            
            // 4. Do whatever we might need with the instance
            instance.someFunctionThatHasAccessToInstanceState()
        }
        
        // 1. Retain `self`, and get an opaque pointer to it
        let retainedPointerToSelf = UnsafeMutableRawPointer(Unmanaged.passRetained(self).toOpaque())
        
        // 2. Register our callback, and pass the pointer to self as `userInfo`
        doSomething(myProgressChangedCallback, retainedPointerToSelf)
    }
}
进一步阅读

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM