You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

适配Kotlin多平台生成的Swift协议并解决Swift并发兼容问题

适配Kotlin多平台生成的Swift协议并解决Swift并发兼容问题

我完全理解你现在的困境——要实现一个无法修改的同步协议,但内部全是异步的Actor操作,还要处理主线程的UI调用,这对刚接触Swift并发的开发者来说确实有点棘手。下面我给你一步步拆解解决方案,尽量用Swift并发的原生方式来处理,避免手动加锁:

核心思路

因为协议要求方法是同步的(不能加async),但我们需要在内部执行异步逻辑,同时阻塞调用者直到逻辑完成,这里的关键是用**Task配合.value属性**:启动一个异步任务,然后等待它完成(.value会阻塞当前线程直到任务结束)。同时处理主线程要求的操作,用MainActor.run来切换到主线程执行。

完整实现代码

class DummyTest: Test {
    private var connection: RTMPConnection? // Actor类型
    private var stream: RTMPStream?         // Actor类型
    private var mixer: MediaMixer?          // Actor类型

    init() {}

    func prepare() {
        // 启动异步任务并等待完成,阻塞当前调用线程
        Task {
            // 1. 必须在主线程创建UI组件,用MainActor.run切换上下文
            let output = await MainActor.run {
                MTHKView(frame: .zero)
            }

            // 初始化所有Actor(Actor初始化是同步操作,没问题)
            self.connection = RTMPConnection()
            guard let connection = self.connection else { return }
            self.stream = RTMPStream(connection: connection)
            guard let stream = self.stream else { return }
            self.mixer = MediaMixer(
                multiCamSessionEnabled: false,
                multiTrackAudioMixingEnabled: false,
                useManualCapture: true
            )
            guard let mixer = self.mixer else { return }

            // 2-4 调用Actor的异步方法,直接用await即可
            await mixer.setVideoOrientation(.landscapeRight)
            await mixer.addOutput(stream)
            await stream.addOutput(output)
        }.value
    }

    func start() {
        do {
            try Task {
                // 先确保所有Actor已初始化,避免强制解包崩溃
                guard let connection = self.connection, let stream = self.stream else {
                    throw NSError(
                        domain: "DummyTest",
                        code: -1,
                        userInfo: [NSLocalizedDescriptionKey: "Actors未完成初始化"]
                    )
                }

                // 5 执行异步连接操作
                var response = try await connection.connect("https://test-live-rtmp.twitch.tv:443")
                print(">>> Connect response: \(response)")

                // 6 执行异步发布操作
                response = try await stream.publish("appId")
                print(">>> Publish response: \(response)")
            }.value
        } catch RTMPConnection.Error.requestFailed(let response) {
            print(">>> RTMPConnection.Error.requestFailed: \(response)")
        } catch RTMPStream.Error.requestFailed(let response) {
            print(">>> RTMPStream.Error.requestFailed: \(response)")
        } catch {
            print(">>> Other.Error: \(error)")
        }
    }

    func stop() {
        Task {
            guard let connection = self.connection else { return }
            // 7 执行异步关闭操作
            try? await connection.close()
        }.value
    }
}

关键注意事项

  1. 绝对避免在主线程调用这些方法:
    如果在主线程调用prepare()/start()/stop(),Task { ... }.value会阻塞主线程,而MainActor.run需要主线程空闲才能执行,这会直接导致死锁。一定要在后台线程调用这些方法,比如:

    // 用DispatchQueue后台线程调用
    DispatchQueue.global().async {
        let test = DummyTest()
        test.prepare()
        test.start()
    }
    
    // 或者用Swift并发的detached Task
    Task.detached {
        let test = DummyTest()
        test.prepare()
        test.start()
    }
    
  2. 避免强制解包:
    我在代码里加了guard let来安全解包Actor实例,避免因为初始化失败导致的崩溃,比直接用!更稳妥。

  3. Actor的线程安全:
    因为connection、stream、mixer都是Actor类型,它们的方法本身已经是线程安全的,我们不需要额外加锁,await会自动处理Actor的串行执行逻辑。

为什么不用手动锁?

你提到考虑过用Lock,但Swift并发的Actor和Task机制已经帮我们处理了线程安全和异步调度,手动加锁反而容易引入死锁、优先级反转等问题,用原生的并发工具更符合Swift的设计理念,也更易维护。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 10:53:07