簡體   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