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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 07:27:03