函数 swift

问题描述

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

我在调用函数中尝试了断点,我仍然可以看到数据,但是之后它变成了 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() 函数返回。 Alamofire.request() 调用触发的网络调用仍未发生,您的关闭代码仍未执行。

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

系统现在调用您的 tableView.numberOfRowsInSection(_:) 函数,在您将新歌曲项安装到 songItems 之前,因此 songItems.count == 0。UIKit 认为您的 table view 为空,并将其显示为空。>

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

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

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

编辑:

想想这样的异步调用:

你正在做晚饭,你意识到你需要面包屑。你让你的孩子去商店买。他们向商店走去。当您要求您的孩子获取尚未获得的面包屑时,您不会立即伸手去拿它们。

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

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

#3 是您将需要面包屑的烹饪步骤放在一边,继续做其他事情。

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