简体   繁体   English

为什么实例变量在闭包内部具有不同的值?

[英]Why does an instance variable have a different value inside of a closure?

func loadYelpComments(){
        guard let business = business else {return}
        guard let _ = tableView else {return}
        guard let businessID = business.id else {return}
        let dateFormatter = DateFormatter()
        dateFormatter.dateStyle = .full

        HTTPHelper.getYelpComments(for: businessID, completionHandler: { data in
            let json = JSON(data)
            let reviews = json["reviews"].arrayValue.map({return $0.dictionaryValue})
            var commentDate: Date?


            for (index, review) in reviews.enumerated(){
                let userDictionary = review["user"]?.dictionary
                if let dateString = review["time_created"]?.stringValue{
                    commentDate = dateFormatter.date(from: dateString)
                }

                let yelpComment = YelpComment(rating: review["rating"]?.intValue, userImageURL: userDictionary?["image_url"]?.stringValue, userName: userDictionary?["name"]?.stringValue, comment: review["text"]?.stringValue, commentDate: commentDate, commentPageURL: review["url"]?.stringValue)

                self.comments.append(yelpComment)
            }

            print("Number of comments: \(self.comments.count)") //Prints: Number of comments: 3"
            self.tableView.reloadData()

        })

            print("Number of comments: \(self.comments.count)") //This prints firt "Number of comments: 0"

    }

The getYelpComments(for:completionHandler:) class method is responsible for fetching JSON data from the Yelp API. getYelpComments(for:completionHandler:)类方法负责从Yelp API提取JSON数据。 To my surprise even though the comments instance array is being updated by appending new YelpComment objects to it, its count has different values inside and outside of the closure. 令我惊讶的是,即使通过将新的YelpComment对象添加到comments实例数组来更新comments实例数组,其count在闭包内部和外部也具有不同的值。

These comments are supposed to be displayed in a table view. 这些注释应该显示在表格视图中。 What further confuses me is the fact that when I add tableView.reloadData() within the closure I get an index out of bounds for an empty array error but when I add the same line of code: tableView.reloadData() out side of the closure I get no error and comments.count equates zero. 令我感到困惑的是,当我在闭包内添加tableView.reloadData() ,我得到了index out of bounds for an empty array错误的index out of bounds for an empty array但是当我添加同一行代码时: tableView.reloadData()关闭我没有错误,并且comments.count等于零。 Any help would be much appreciated! 任何帮助将非常感激!

PS I don't know if mentioning this is going to make any difference but data is @escaping . PS我不知道是否提及这一点会有所不同,但是data@escaping I am also using SwiftyJSON and Alamofire . 我也在使用SwiftyJSONAlamofire

UPDATE: 更新:

My table view data source methods are: 我的表视图数据源方法是:

   override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        if section == 0 {
            return super.tableView(tableView, numberOfRowsInSection: section)
        } else {
            return comments.count
        }

    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        if indexPath.section == 0 {
            return super.tableView(tableView, cellForRowAt: indexPath)
        } else {
            let cell = tableView.dequeueReusableCell(withIdentifier: CustomCellTypeIdentifiers.YelpCommentCell, for: indexPath) as! YelpCommentCell
            cell.configureYelpCell(with: comments[indexPath.row])
            return cell
        }
    }

Because the job of the completion block is to report to you when the network call is done. 因为completion模块的工作是在网络调用completion向您报告。 HTTPHelper is making a call to a server, which can take some considerable amount of time. HTTPHelper正在调用服务器,这可能需要花费大量时间。 It does not stop your program from executing, and just wait at that line: it goes to the next line, immediately, and calls print("Number of comments: \\(self.comments.count)") , and you get 0, because the network call isn't done yet. 它不会阻止您的程序执行,而只是在那一行等待:它立即转到下一行,并调用print("Number of comments: \\(self.comments.count)") ,您得到0,因为尚未完成网络通话。 Then, some time later, that whole completion block happens. 然后,过了一段时间,整个completion块发生了。

This is called an asynchronous function , and it commonly trips up people who haven't seen it before, but you see lots of it eventually, so it's worth reading up on. 这被称为异步函数 ,它通常会跳闸以前从未见过的人,但是最终您会看到很多,因此值得继续阅读。

The fact that you say "when I add tableView.reloadData() within the closure I get an index out of bounds for an empty array error", it sounds like one of your UITableViewDataSource functions that configures the table ( cellForRowAt , numberOfRowsInSection ), has an error in it somewhere. 您说“当我在闭包内添加tableView.reloadData()时,我得到一个index out of bounds for an empty array错误的index out of bounds for an empty array事实”,这听起来像是您配置表的UITableViewDataSource函数之一( cellForRowAtnumberOfRowsInSection ),一个地方的错误。

Closures are not necessarily executed immediately at the point where you see them. 关闭不一定在您看到它们时立即执行。

In your code, the print outside the closure will be executed before the print inside the closure. 在你的代码中, print关闭外将之前执行print瓶盖内。 This is because getYelpComments is what is called an "asynchronous method". 这是因为getYelpComments是所谓的“异步方法”。 While you are waiting for the Yelp servers to respond, the code doesn't just sit there doing nothing. 在等待Yelp服务器响应时,代码并不只是坐在那里无所事事。 It continues to execute the code after the closure, printing that the count is 0. 关闭后,它将继续执行代码,并打印计数为0。

After Yelp responds, the closure gets executed. Yelp响应后,将执行关闭。 And count, being 3, is printed. 然后计数为3。

As for why putting tableView.reloadData() causes it to crash, there is probably something wrong in your table view datasource methods. 至于为什么放置tableView.reloadData()导致它崩溃,则您的表视图数据源方法中可能存在问题。 It is most likely an off-by-1. 它很可能是1比1。

Edit: 编辑:

I think the fact that you write 我认为你写的事实

if section == 0 {
    return super.tableView(tableView, numberOfRowsInSection: section)

is weird. 很奇怪。 I don't know why you want to return the super implementation if the section is 0. Does your table view have multiple sections? 我不知道为什么如果该部分为0,为什么要返回super实现。您的表视图是否有多个部分? If not, you should probably remove the check. 如果没有,您应该删除支票。 If yes, then properly return a value for the first section. 如果是,则为第一部分正确返回一个值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM