Ios 在Swift中完成CloudKit异步操作时设置UILabel文本

Ios 在Swift中完成CloudKit异步操作时设置UILabel文本,ios,swift,swift3,Ios,Swift,Swift3,我对Swift和异步代码都是新手,所以请告诉我这是否太离谱了。基本上我想: 打开应用程序 这会触发对CloudKit记录的读取 读取完成后,UILabel将显示检索到的记录数 这本身显然没有什么用处,但作为一项原则,它将帮助我理解异步代码操作,以及如何在操作完成时触发操作 // In ViewController Swift file: class ViewController: UIViewController{ override func viewDidLoad() {

我对Swift和异步代码都是新手,所以请告诉我这是否太离谱了。基本上我想:

  • 打开应用程序
  • 这会触发对CloudKit记录的读取
  • 读取完成后,UILabel将显示检索到的记录数
  • 这本身显然没有什么用处,但作为一项原则,它将帮助我理解异步代码操作,以及如何在操作完成时触发操作

    // In ViewController Swift file:
    
    class ViewController: UIViewController{
    
        override func viewDidLoad() {
            super.viewDidLoad()
            readDatabase()
        }
        @IBOutlet weak var myLabel: UILabel!
    }
    
    let VC=ViewController()
    
    
    //In Another Swift file:
    
    func readDatabase() {
        let predicate = NSPredicate(value: true)
        let query = CKQuery(recordType: "myRecord", predicate: predicate)
        let container = CKContainer.default()
        let privateDB = container.privateCloudDatabase
    
        privateDB.perform(query, inZoneWith:nil) { (allRecs, err) in
            VC.myLabel.text = ("\(allRecs?.count) records retreived")
    
        /*
        ERROR OCCURS IN LINE ABOVE:
           CONSOLE: fatal error: unexpectedly found nil while unwrapping an Optional value
           BY CODE LINE: Thread 8:EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) 
        */
    
        }
    }
    
    我可以从viewDidLoad函数中设置文本字段,那么为什么不从该函数中调用的函数中设置呢

    我还尝试了其他一些方法:

    • 使用异步调度将其放在线程1上
    • 在ViewController类中使用实现var,使用设置文本的didSet,将var设置为privateDB中的所需值。执行代码以触发更改
    这两者都会产生与上述相同的问题

    是的,我知道在.perform中没有任何错误处理,而且有记录。如果在加载视图几秒钟后手动将UILabel文本设置为记录计数,则效果良好

    所以问题是…… 如何使用数据库读取的完成作为触发器将记录的属性加载到视图中


    谢谢

    这一行有问题:
    让VC=ViewController()
    。在这里,您将实例化ViewController类的一个新实例,并尝试在新创建的实例上设置标签。但是,您可能希望在当前显示的viewController实例上设置标签


    只要将这一行
    VC.myLabel.text=(“\(allRecs?.count)records retreved”)
    更改为
    self.myLabel.text=(“\(allRecs?.count)records retreved”)
    就可以了。

    明白了,解决方案如下:

    // In ViewController Swift file:
    
    typealias CompletionHandler = (_ recCount:Int,_ err:Error?) -> Void
    
    class ViewController: UIViewController{
    
        override func viewDidLoad() {
            super.viewDidLoad()
            readDatabase(completionHandler: { (recCount,success) -> Void in
                if err == nil {
                     self.myLabel.text = "\(recCount) records loaded"
                } else {
                     self.myLabel.text = "load failed: \(err)"     
                }
            })
        }
        @IBOutlet weak var myLabel: UILabel!
    }
    
    
    //In Another Swift file:
    
    func readDatabase() {
        let predicate = NSPredicate(value: true)
        let query = CKQuery(recordType: "myRecord", predicate: predicate)
        let container = CKContainer.default()
        let privateDB = container.privateCloudDatabase
    
        privateDB.perform(query, inZoneWith:nil) { (allRecs, err) in
            if let recCount = allRecs?.count {
                completionHandler(recCount,err)
            } else {
                completionHandler(0,err)
            }
        }
    }
    
    这与原始的区别在于,它在函数调用中使用CompletionHandler typealias来加载数据库记录,这将返回记录计数和可选错误

    完成操作现在可以存在于ViewController类中,并使用self.myLabel访问UILabel,这解决了之前发生的错误,同时将数据库加载代码与ViewController类分开


    此版本的代码还具有基本的错误处理。

    您还可能(不确定,因为我从未使用过此框架)更新主线程中的标签(闭包位于后台线程中并不奇怪)。将其更改为self.mylabel会导致“使用未解析标识符”self”错误。这是因为readDatabase()函数不在viewController范围内吗?是的,这就是原因,我没有看到关于它在单独类中的注释,抱歉