函数 swift 后值变为空

value becomes null after function swift

我已经练习了几天 swift,只是在调用函数 getItunesData() 后模型 var songItems: [SongItem] = [] 变为 nil 时遇到了这个问题。

我已经在调用函数中尝试了断点我仍然可以看到数据,但是之后它变成了零。

希望你们能帮我解决这个问题。干杯

import UIKit
import Alamofire
import SwiftyJSON

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    @IBOutlet weak var tableView: UITableView!
    var songItems: [SongItem] = []

    let city = ["one","two","three"]
    let params = ["term":"marroon5",
                  "limit":"5"]
    let url: String = "https://itunes.apple.com/search?"

    
    override func viewDidLoad() {
        super.viewDidLoad()
        getItunesData()

        tableView.delegate = self
        tableView.dataSource = self
        tableView.register(UINib(nibName: "customCell", bundle: nil), forCellReuseIdentifier: "customCell")
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return songItems.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! customCell
        
        cell.labelTxt.text = songItems[indexPath.row].releaseDate
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    }
    
    func getItunesData() {
        Alamofire.request(url, method: .get, parameters: params).responseJSON { response in
            if response.result.isSuccess {
                let json = JSON(response.result.value)
                self.songItems = AllSongsModel(json: json).allSongsArray
            } else {
                
            }}
    }
}

我认为您对异步代码的工作原理感到困惑。此代码:

override func viewDidLoad() {
    super.viewDidLoad()
    //#1
    getItunesData()
    //#4
    tableView.delegate = self
    tableView.dataSource = self
    tableView.register(UINib(nibName: "customCell", bundle: nil), forCellReuseIdentifier: "customCell")
}

func getItunesData() {
    //#2
    Alamofire.request(url, method: .get, parameters: params).responseJSON 
    //-----------------------------------------
    //The code in the braces below does not execute until later.
    { response in
        //#5
        if response.result.isSuccess {
            let json = JSON(response.result.value)
            self.songItems = AllSongsModel(json: json).allSongsArray
            //#6: Add a call to reloadData() here, once the data is ready.
            tableView.reloadData()

        } else {
            
        }
    }
    //-----------------------------------------
    //#3
}

执行如下:

#1,您在 viewDidLoad 中,即将调用 getItunesData()

#2,在 getItunesData() 中调用 Alamofire.request(),传入请求函数保存的 完成处理程序 以便稍后调用,一旦请求完成了。我用 //---------- 破折号括起来的大括号中的代码被保存起来以备后用,不会立即执行。

接下来,执行转到#3。 (Alamofire.request() 调用后的代码。)那里没有代码,所以 getItunesData() 函数 returns。 Alamofire.request()调用触发的网络调用还没有发生,你的关闭代码还没有执行。

#4,接下来执行viewDidLoad()中的行。您的整个 viewDidLoad() 函数完成并且 returns。来自您的Alamofire.request()(及其完成处理程序)的网络调用仍然没有发生。

系统现在调用你的 tableView.numberOfRowsInSection(_:) 函数,在你将新的歌曲项目安装到 songItems 之前,所以 songItems.count == 0。UIKit 认为你的 table 视图是空的,并将其显示为空。

在将来的某个时间,可能是几秒钟后,您的 Alamofire.request() 触发的网络调用完成,Alamofire 调用您传递给它的闭包。 现在您在#5 处的关闭代码执行。假设网络调用成功,您将结果安装到 self.songItems

正如 Sh_Khan 指出的那样,您应该在完成处理程序中调用 tableView.reloadData()(上面代码中的#6)

如果不在您的完成处理程序中添加对 tableView.reloadData() 的调用,您永远不会告诉 table 视图现在有数据要显示,而且它看起来是空的。

编辑:

想想这样的异步调用:

您正在做晚饭,发现您需要面包屑。你让你的孩子去商店买。他们前往商店。您不会在让您的孩子拿到面包屑后立即伸手去拿 not-yet-acquired 面包屑。

您继续执行其他任务并等待它们 return。 kid 运行 差事是异步任务。来自您孩子的“这是您的面包屑”是完成处理程序触发。在完成处理程序中,您返回并完成需要面包屑的那部分食谱。

上面代码中的#2(对 Alamofire.request 的调用)是你让你的孩子捡起一些面包屑。

#3 是否将需要 bread-crumbs 的烹饪步骤搁置一旁,继续做其他事情。

#6(完成处理程序中的代码)是当您的孩子回来并说“这是您要的面包屑”时。在 #6 中,您获取面包屑并使用它们完成您等待的那一步。