[英]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
. 我也在使用
SwiftyJSON
和Alamofire
。
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函数之一( cellForRowAt
, numberOfRowsInSection
),一个地方的错误。
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.