Swift + Realm:如何在collection.find作用域内修改外部变量的值
问题根因
你遇到的不是变量作用域问题,是异步执行时序问题:collection.find是异步网络请求方法,它的回调闭包会等MongoDB服务端返回结果之后才会执行,而你写在collection.find后面的print(self.bar)会在发起请求之后立刻执行,这时候回调还没触发,self.bar还没被赋值,所以才会输出false,不是修改没生效。
解决方案
可以用回调闭包、async/await(iOS 15+)来处理异步时序,这里给两种常用实现方案:
方案1:自定义回调闭包(兼容性最好,支持全版本iOS)
调整foo1方法增加完成回调,所有依赖bar最新值的操作都放在回调里执行:
import RealmSwift import Darwin import SwiftUI let app = App(id: "my-app-id") class AccessManager: Object { @objc dynamic var isInTime: Bool = false @objc dynamic var bar: Bool = false // 原代码漏了bar的属性定义,这里补充 func foo2() -> Bool { return true } // 增加完成回调参数,请求完成后把最新的bar值回传 func foo1(completion: @escaping (Bool) -> Void) { app.login(credentials: Credentials.anonymous) { (result) in DispatchQueue.main.async { switch result { case .failure(let error): print("Login failed: \(error)") completion(false) case .success(let user): print("Login as \(user) succeeded!") let client = app.currentUser!.mongoClient("mongodb-atlas") let database = client.database(named: "my-database") let collection = database.collection(withName: "my-collection") let identity = "my-identity" collection.find(filter: ["_partition": AnyBSON(identity)], { (result) in DispatchQueue.main.async { // 保证属性修改和后续操作都在主线程执行 switch result { case .failure(let error): print("Call to MongoDB failed: \(error.localizedDescription)") completion(false) case .success(let documents): self.bar = self.foo2() print("回调内bar值:\(self.bar)") // 输出true completion(self.bar) // 赋值完成后触发回调 } } }) // 此处不要执行依赖bar最新值的操作,此时回调还未触发 } } } } // 调用示例 func testCall() { foo1 { [weak self] updatedBar in guard let self = self else { return } print("回调外拿到的最新bar值:\(updatedBar)") // 输出true // 所有依赖bar最新值的逻辑都要写在这个回调块里 } } }
方案2:async/await(iOS 15+,代码更简洁无嵌套)
Realm SDK已支持Swift异步并发特性,不用嵌套回调,逻辑更清晰:
@available(iOS 15.0, *) func foo1Async() async -> Bool { do { let user = try await app.login(credentials: .anonymous) print("Login as \(user) succeeded!") let client = user.mongoClient("mongodb-atlas") let database = client.database(named: "my-database") let collection = database.collection(withName: "my-collection") let identity = "my-identity" let _ = try await collection.find(filter: ["_partition": AnyBSON(identity)]) self.bar = foo2() print("异步方法内bar值:\(self.bar)") return self.bar } catch { print("操作失败:\(error.localizedDescription)") return false } } // 调用示例 @available(iOS 15.0, *) func testAsyncCall() { Task { let updatedBar = await foo1Async() print("异步方法外拿到的bar值:\(updatedBar)") // 输出true } }
内容的提问来源于stack exchange,提问作者user3980558
相关产品推荐
相关产品推荐

