Swift 不理解此代码中的完成处理程序
“完成(项目)”的目的是什么?我知道,当我们不知道一个操作什么时候才能完成时(比如下载时间),就会使用这些工具。但是,在本例中,loadData()只是从项目目录中提取一个plist文件,所以我觉得这是一个固定的时间,不需要完成处理程序。另外,我的课本上说它返回annotations数组,但我没有看到任何return语句。我是swift的新手,所以如果这不是一个好问题,我道歉Swift 不理解此代码中的完成处理程序,swift,completionhandler,Swift,Completionhandler,“完成(项目)”的目的是什么?我知道,当我们不知道一个操作什么时候才能完成时(比如下载时间),就会使用这些工具。但是,在本例中,loadData()只是从项目目录中提取一个plist文件,所以我觉得这是一个固定的时间,不需要完成处理程序。另外,我的课本上说它返回annotations数组,但我没有看到任何return语句。我是swift的新手,所以如果这不是一个好问题,我道歉 func fetch(completion: (_ annotations: [RestaurantItem]) -&g
func fetch(completion: (_ annotations: [RestaurantItem]) -> ()) {
if items.count > 0 { items.removeAll() }
for data in loadData() {
items.append(RestaurantItem(dict: data))
}
completion(items)
}
是的,带有完成处理程序的函数不需要返回语句——只需调用完成处理程序(
completion(items)
)
那么您知道函数参数如何接受String
s、Int
s等
func doSomething(inputThing: Int) {
^ this is the type (an Int)
}
他们也可以接受闭包。在您的示例中,completion
参数接受闭包
func fetch(completion: (_ annotations: [RestaurantItem]) -> ()) {
^ this is the type (a closure)
}
闭包基本上是可以传递的代码块。通常,如果函数接受闭包作为参数,则将闭包称为“完成处理程序”(因为它通常在函数末尾被调用)
您的闭包还指定类型为[RestaurantItem]
的输入和()(Void)
的输出(Void,因为闭包本身不会返回任何内容)。注释:部分是不必要的:只需执行以下操作:
func fetch(completion: ([RestaurantItem]) -> ()) {
}
调用函数时,需要传入闭包,并将输入分配给变量
fetch(completion: { restaurantItems in
/// do something with restaurantItems (assigned to the input)
})
您将在func fetch(completion:(u.annotations:[RestaurantItem])->())的末尾调用此闭包
调用completion(items)
将items
传递到闭包的输入中,该闭包被分配给restaurantItems
通常闭包用于运行需要时间的函数,如下载文件。但是在您的示例中,loadData()
看起来会立即发生,所以您应该使用一个带有返回类型的普通函数
func fetch() -> [RestaurantItem] {
if items.count > 0 { items.removeAll() }
for data in loadData() {
items.append(RestaurantItem(dict: data))
}
return items
}
let restaurantItems = fetch()
是的,带有完成处理程序的函数不需要返回语句——只需调用完成处理程序(completion(items)
)
那么您知道函数参数如何接受String
s、Int
s等
func doSomething(inputThing: Int) {
^ this is the type (an Int)
}
他们也可以接受闭包。在您的示例中,completion
参数接受闭包
func fetch(completion: (_ annotations: [RestaurantItem]) -> ()) {
^ this is the type (a closure)
}
闭包基本上是可以传递的代码块。通常,如果函数接受闭包作为参数,则将闭包称为“完成处理程序”(因为它通常在函数末尾被调用)
您的闭包还指定类型为[RestaurantItem]
的输入和()(Void)
的输出(Void,因为闭包本身不会返回任何内容)。注释:
部分是不必要的:只需执行以下操作:
func fetch(completion: ([RestaurantItem]) -> ()) {
}
调用函数时,需要传入闭包,并将输入分配给变量
fetch(completion: { restaurantItems in
/// do something with restaurantItems (assigned to the input)
})
您将在func fetch(completion:(u.annotations:[RestaurantItem])->())的末尾调用此闭包
调用completion(items)
将items
传递到闭包的输入中,该闭包被分配给restaurantItems
通常闭包用于运行需要时间的函数,如下载文件。但是在您的示例中,loadData()
看起来会立即发生,所以您应该使用一个带有返回类型的普通函数
func fetch() -> [RestaurantItem] {
if items.count > 0 { items.removeAll() }
for data in loadData() {
items.append(RestaurantItem(dict: data))
}
return items
}
let restaurantItems = fetch()
我们通常在编写异步代码时使用完成处理程序闭包,也就是说,在我们开始一些耗时的事情(例如,网络请求)的情况下,但您不希望在这个相对缓慢的网络请求发生时阻止调用方(通常是主线程)
那么,让我们来看一个典型的完成处理程序模式。假设您正在使用URLSession
执行异步网络请求:
func fetch(completion: @escaping ([RestaurantItem]) -> Void) {
let task = URLSession.shared.dataTask(with: url) { data, response, error in
// parse the `data`
let items: [RestaurantItem] = ...
DispatchQueue.async { completion(items) }
}
task.resume()
}
(我使用URLSession
作为异步进程的示例。显然,如果您使用的是Alamofire或Firebase或任何异步API,想法都是一样的。异步请求完成时,我们称完成处理程序闭包为completion
)
这将启动网络请求,但会立即返回,当网络请求稍后完成时,它将调用completion
。注意,fetch
不应直接更新模型。它只是将结果提供给闭包
调用方(可能是视图控制器)承担更新模型和UI的责任,之后调用completion
closure时:
var items: [RestaurantItems] = [] // start with empty array
override func viewDidLoad() {
super.viewDidLoad()
fetch { items in
print("got items", items)
self.items = items // this is where we update our model
self.tableView.reloadData() // this is where we update our UI, a table view in this example
}
print("finishing viewDidLoad")
}
如果我们观察控制台,我们将在“GetItems”消息之前看到“finishing viewDidLoad”消息。但是我们提供给fetch
的闭包会更新模型并触发重新加载UI
这是一个过于简化的示例,但这是完成处理程序闭包的基本思想,它允许我们提供一个代码块,在异步任务完成时可以执行该代码块,同时允许fetch
立即返回,这样我们就不会阻塞UI
但是,我们使用这种复杂的闭包模式的唯一原因是,fetch
执行的任务是异步运行的。如果fetch
没有执行异步操作(在您的示例中似乎没有),我们根本不会使用此闭包模式。您只需返回结果
那么,让我们回到你的例子
有几个问题:
更新项并返回结果(无论是直接返回还是使用闭包)是没有意义的。你可以做一个或另一个,但不能同时做两个。因此,我可能建议您创建一个局部变量,并在闭包中传递结果(与上面的异步模式非常相似)。例如:
func fetch(completion: (_ annotations: [RestaurantItem]) -> ()) {
var items: [RestaurantItem] = []
for data in loadData() {
items.append(RestaurantItem(dict: data))
}
completion(items)
}