变量未正确保存

Posted

技术标签:

【中文标题】变量未正确保存【英文标题】:Variable is not saved correctly 【发布时间】:2021-03-15 05:28:35 【问题描述】:

在函数中,我将数据从 json 字符串解析到字典,然后将其保存到变量中。但是当我尝试从另一个函数调用变量时,数据没有保存。

代码是:

    var usersData: [String: NSArray] = [:]

    @IBAction func buttonTapped(_ sender: UIButton) 
                
        if let url = URL(string: "https://medaljson.aadev151.repl.co/get-data") 
            URLSession.shared.dataTask(with: url)  data, response, error in
              if let data = data 
                 if let jsonString = String(data: data, encoding: .utf8) 
                    let data: Data? = jsonString.data(using: .utf8)
                    let json = (try? JSONSerialization.jsonObject(with: data!, options: [])) as? [String:NSArray]
                    
                    self.usersData = json!

                 
               
           .resume()
        
        
        if shouldPerformSegue(withIdentifier: "loginSegue", sender: nil) 
            performSegue(withIdentifier: "loginSegue", sender: nil)
        
        
    
    
    override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool 
        
        if identifier == "loginSegue" 
            
            print(self.usersData)    // [:]
            
            let name = nameTF.text ?? ""
            let surname = surnameTF.text ?? ""
            let ssa = ssaTF.text ?? ""
            
            if usersData[ssa] == nil 
                errorLabel.text = "SSA not found"
                return false
                
             else if usersData[ssa]![0] as! String != name || usersData[ssa]![0] as! String != surname 
                errorLabel.text = "Name / Surname doesn't match"
                return false
                
             else 
                return true
                
            
            
        
        return true
        
    

我想知道如何解决问题。 感谢您的帮助:)

【问题讨论】:

虽然与您的具体问题无关,这已经在下面得到了回答,但值得指出的是,如果 JSON 解码失败,像这样 (self.usersData = json!) 的强制展开 json 将使您的程序崩溃。您可能需要查看if letdo/try/catch 以避免这种潜在的崩溃。 也不相关:永远不要自己调用包含willdidshould 的方法。它们仅由框架调用。并且根本不要在 Swift 中使用 NS... 集合类型。 @jnpdx 我知道,这段代码只是一个测试。但无论如何,谢谢:) 【参考方案1】:

URLSession.shared.dataTask(with 调用完成块/闭包异步,因为您更新闭包内的self.usersData = json!self.userData 也会异步更新,当您调用if shouldPerformSegue(withIdentifier: "loginSegue", sender: nil) 作为立即下一条语句时URLSession.shared.dataTask(with (同步) 所以运行时执行 if 语句和 performSegue(withIdentifier: 甚至在执行闭包之前因此当 shouldPerformSegue(withIdentifier 被执行时你的数据不会更新

        if let url = URL(string: "https://medaljson.aadev151.repl.co/get-data") 
            URLSession.shared.dataTask(with: url)  data, response, error in
                if let data = data 
                    if let jsonString = String(data: data, encoding: .utf8) 
                        let data: Data? = jsonString.data(using: .utf8)
                        let json = (try? JSONSerialization.jsonObject(with: data!, options: [])) as? [String:NSArray]

                        self.usersData = json! //as mentioned by jnpdx in comment above, this statement is error prone. Because its out of scope of the question I havent updated it, if you need to know how to assign the value safely, lemme know
                        DispatchQueue.main.async 
                            if self.shouldPerformSegue(withIdentifier: "loginSegue", sender: nil) 
                                self.performSegue(withIdentifier: "loginSegue", sender: nil)
                            
                        
                    
                
            .resume()
        

编辑:随着 cmets 中不相关但重要的点越来越多,我相信我在回答中也解决所有这些问题对我来说是唯一正确的,尽管它们与问题本身并不严格相关.上面的答案应该为 OP 的问题提供正确的解释,但同时也需要解决其他问题。

        if let url = URL(string: "https://medaljson.aadev151.repl.co/get-data") 
            URLSession.shared.dataTask(with: url)  data, response, error in
                if let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] 
                    self.usersData = json

                    DispatchQueue.main.async 
                        if self.shouldPerformLoginSegue() 
                            self.performSegue(withIdentifier: "loginSegue", sender: nil)
                        
                    
                
            .resume()
        
func shouldPerformLoginSegue() -> Bool 
        let name = nameTF.text ?? ""
        let surname = surnameTF.text ?? ""
        let ssa = ssaTF.text ?? ""

        if usersData[ssa] == nil 
            errorLabel.text = "SSA not found"
            return false

         else if usersData[ssa]![0] as! String != name || usersData[ssa]![0] as! String != surname 
            errorLabel.text = "Name / Surname doesn't match"
            return false

         else 
            return true

        
    

几件事,

    您已经在使用try?,因此您无需包装 docatch 中的声明,但我真的希望你知道 它的后果,如果 JSONSerialisation 出于任何原因结束 向上抛出错误,try? 将返回值nil,并且 因为你将let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] 代码放入 if let 条件永远不会被执行。

    这是一种处理方式,另一种是捕获错误并处理 如果您决定使用,则适当地(根据您的用例) 该方法改用trytry! 的另一个变种 你可以阅读并决定哪一个对你来说更有意义。

    正如 Vadian 提到的,实际上没有必要使用任何 NS 集合类型,而是可以使用它们的 swift 对应物,在 在这种情况下Array,数组采用通用参数Element 和 因为在这种情况下它不能隐式推断Element 的类型, 它要求您明确指定,因为我不完全确定 json 结构我建议你可以使用Array<Any> 但你的 shouldPerformSegue(withIdentifier 中的代码让我失望,我 不要认为你的代码可以处理数组,看起来你是 期待一本字典,因为我也无法推断出类型 选择[String: Any] 随意设置

    再次如 Vadian 所述,您永远不应该手动调用 shouldPerformSegue(withIdentifier:,您可以覆盖它 并且显然可以返回一个布尔值,指示是否 是否应该执行特定的segue,但你不应该调用 手动。

    shouldPerformSegue(withIdentifier: 仅在以下情况下被调用 storyboard segues 并且在你调用时不会被调用 performSegue(withIdentifier: 手动/显式。阅读link了解更多信息。

    我了解您只想在确定时执行转场 条件都满足了,但是很明显当你打电话的时候 performSegue(withIdentifier:ios 假设你已经 执行了各种检查,并且您想明确执行 segue 因此它不会调用shouldPerformSegue(withIdentifier: 再次检查。

    因此,您需要在显式调用之前处理这些检查 performSegue(withIdentifier: 本身因此我添加了一个新的 实例方法shouldPerformLoginSegue() -> Bool 告诉你 您是否应该执行特定的 loginSegue

    在您的代码中 if let jsonString = String(data: data, encoding: .utf8) let data: Data? = jsonString.data(using: .utf8) 是 完全没有必要,将数据转换为 JSON 毫无意义 字符串并再次将其重新转换为Data 并将此数据传递给 JSONSerialization你已经有来自数据任务的数据 响应,只需将其传递给JSONSerialization

    我当前对shouldPerformLoginSegue 的实现有很多副作用,如果您打算遵循pure functional programmingconcern separation 的原则,绝对不是正确的方法,但这里的想法只是为了展示您如何将您的代码 shouldPerformSegue 移动到您自己的另一个实例函数中,并在调用 performSegue 之前将其用作检查

【讨论】:

@vadian:感谢您的 cmets,我更新了我的答案,并添加了更多关于 OP 应该做什么以及原因的解释:) 太棒了!非常感谢!你能告诉我为什么来自 TF 和 JSON 数据的完全相同的字符串可能不相等吗? 感谢您的回答,我不确定您的问题是什么,请您详细说明一下吗? “请告诉我为什么来自 TF 和 JSON 数据的完全相同的字符串可能不相等?” TF 中的字符串是什么意思,抱歉,我不确定 TF 的首字母缩写词:| 是的,当然,我会澄清的。我的意思是当我调用 shouldPerformLoginSegue() 并打印数据时,它说surname(文本字段的值)和usersData[ssa]![0](字典的值)并不相等,而实际上它们是相等的。我知道这听起来可能仍然令人困惑,所以如果确实如此,请告诉我:) 谢谢你的帮助和所有的解释,我真的很感激 :) 但实际上我发现我并不真的需要第二个文本字段,所以我会离开程序只有一个,可以正常工作:)

以上是关于变量未正确保存的主要内容,如果未能解决你的问题,请参考以下文章

画布中的图像未正确保存

readLine() 未正确保存

Swift NSCache 结构未正确保存

核心数据一对多关系未正确/按预期保存

MongooseJS 未正确保存到数组

CoreData 中的 NSData 未正确保存