Swift中如何筛选Actor对象?迭代遇无限循环及隔离错误
解决方案
问题1:filter陷入无限循环的原因
原AsyncSequence实现中,next()方法每次调用都会重新创建data.makeIterator(),导致每次都返回集合的第一个元素,filter会持续处理同一个元素,进而引发无限循环。
问题2:Actor隔离属性访问错误的原因
makeAsyncIterator()是**非隔离(nonisolated)**方法,无法直接访问Actor的隔离属性data——Actor的属性仅允许在其自身隔离上下文中访问。
正确实现方案
我们需要让迭代器基于data的快照(副本)进行遍历,同时确保快照的获取操作在Actor隔离上下文中完成:
actor DataModel { typealias Details = (passed: Bool, scores: [Int]) private(set) var data: [Int: Details] = [:] func update(_ value: (Bool, [Int]), forKey key: Int) { data.updateValue(value, forKey: key) } subscript(id: Int) -> Details? { get { data[id] } set { data[id] = newValue } } func removeAll() { data.removeAll() } // 异步返回data的快照,确保在Actor隔离上下文访问data private func snapshot() async -> [Int: Details] { data } } extension DataModel: AsyncSequence { typealias Element = (key: Int, value: Details) nonisolated func makeAsyncIterator() -> AsyncIterator { AsyncIterator(actor: self) } struct AsyncIterator: AsyncIteratorProtocol { private let actor: DataModel private var underlyingIterator: Dictionary<Int, Details>.Iterator? mutating func next() async throws -> Element? { // 首次调用时获取data快照并创建迭代器 if underlyingIterator == nil { let snapshot = await actor.snapshot() underlyingIterator = snapshot.makeIterator() } // 复用同一个迭代器,避免无限循环 return underlyingIterator?.next() } } }
测试验证
调用filter和遍历的代码可以正常工作,不会再出现无限循环:
let data = DataModel() await data.update((false, [1, 2]), forKey: 0) await data.update((true, [3, 4, 5]), forKey: 1) await data.update((false, [6,7,8,9]), forKey: 2) let filtered = data.filter { el in el.value.passed || el.value.scores.count > 3 } for try await i in filtered { print(i) } // 输出结果: // (key: 1, value: (passed: true, scores: [3, 4, 5])) // (key: 2, value: (passed: false, scores: [6, 7, 8, 9]))
方案说明
- 快照机制:每个迭代器在首次调用
next()时,会异步获取当前data的快照,后续遍历都基于这个快照,避免迭代过程中data被修改导致的不一致。 - Actor隔离合规:通过Actor内部的异步方法
snapshot()访问data,确保所有对隔离属性的操作都在Actor上下文内完成。 - 避免无限循环:迭代器复用同一个底层字典迭代器,遍历完成后会返回
nil,终止循环。
内容的提问来源于stack exchange,提问作者Kevvv
相关产品推荐
相关产品推荐

