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

Swift-NIO:数组中Receiver结构体的Scheduled实例无法持久化问题

问题成因
  • 值类型的复制特性:Receiver是结构体(值类型),执行receivers.append(entry)时,数组存储的是entry的副本而非原实例引用。后续通过inout修改的是最初的局部变量entry,和数组里的副本完全无关,所以数组中的Receiver永远不会更新timeout属性。
  • 任务执行独立于实例:scheduleTask提交到EventLoop后,任务由EventLoop独立管理,哪怕原结构体实例被销毁,任务仍会正常触发,但数组里的副本因未持有timeout引用,无法对任务执行取消操作。
修复方案

把Receiver从结构体改为类(引用类型),数组将存储实例的引用,所有对实例的修改都会直接作用于数组中的同一实例。

修改后的代码示例:

// 将struct改为class
class Receiver {
    var timeout: Scheduled<Any>?
    // 保留原有其他属性
}

private var receivers: Array<Receiver>

func connect() {
    let entry = Receiver(
        // ...初始化属性
    )
    receivers.append(entry)
    connect(entry) // 无需inout
}

func connect(_ device: Receiver) {
    startTimeout(device) // 无需inout
}

private func startTimeout(_ entry: Receiver) {
    stopTimeout(entry)
    
    // 直接操作实例引用,避免不必要的复制
    entry.timeout = entry.eventLoop.scheduleTask(in: .seconds(10), { [weak self, weak entry] in
        print("Receiver timeout occurred")
        guard let self = self, let entry = entry else { return 0 }
        self.reconnect(entry)
        return 0
    })
}

func someStuff() {
    // 现在可正常访问数组中的实例并取消超时
    if let entry = receivers.first {
        stopTimeout(entry)
    }
}

private func stopTimeout(_ entry: Receiver) {
    entry.timeout?.cancel()
    entry.timeout = nil
}

额外注意

  • 闭包中使用[weak self, weak entry]避免循环引用,防止内存泄漏。
  • 移除所有不必要的inout参数,引用类型默认是引用语义传递,无需通过inout修改实例。

内容的提问来源于stack exchange,提问作者cj-

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 03:46:04