为什么Microsoft Azure(或一般的Swift)无法更新变量以在表查询后返回?

我一直在关注成功查询表的Microsoft Azure 文档 (插入,读取和更新项目到数据库工作正常),但在一个简单的方法结束时,直接关闭文档:

func getAllEventIDs() -> [String] { var events:[String] = [] //this is to be updated let delegate = UIApplication.sharedApplication().delegate as! AppDelegate let client = delegate.client! //boiler-plate for azure let itemTable = client.tableWithName("Events") itemTable.query().readWithCompletion { //something about this query block might be preventing successful initialization to the events array (result:MSQueryResult!, error) in //usually error-checking here for item in result.items { events.append(item.valueForKey("id") as! String) //returning events here... } //...and here (almost) work, since Swift expects a return of type Void } return events //still empty } 

我可能不会将数组作为参数传递 ,因为.append函数会改变该数组。

返回的值只是初始的空数组。 但是,这个问题似乎很大程度上源于Azure的查询代码块,而不是来自Swift本身。

一个更简单的例子:

 func returnValueArray() -> [Int] { var array:[Int] = [0,0,0,0] var num:Int = 3 for var n = 0; n < array.count; n++ { array[n] = num } return array } 

返回[3,3,3,3]。 同样,不是Swift的问题,但Swift可能已经体现了Azure的回归问题。

如何在查询方法结束时返回所需的更新数组? 是否可以将可变数组传入方法,追加值,然后返回该数组?

您问:“如何在查询方法结束时返回所需的更新数组?” 简短的回答:你做不到。

这是异步编码的基础。

readWithCompletion方法是异步的。 它将您在后台处理的请求排队,并立即返回。

在您的读取请求甚至开始处理之前,您的return events //still empty代码。

您需要重构getAllEventIDs方法以将完成块作为参数。 该完成块将传递给您的events数组。 然后在readWithCompletion的完成块内部,您getAllEventIDs方法调用完成块。

所以当你调用getAllEventIDs ,你会传递一个完成块来完成你需要对events数组做的事情。

编辑:

我创建了一个名为SwiftCompletionHandlers的Github项目,该项目说明了这一点以及如何处理它。 它有一个样本类AsyncManager ,它模拟异步下载。

https://github.com/DuncanMC/SwiftCompletionHandlers

它有一个如下所示的方法:

 func asyncFetchImage(#imageName: String, completion: ( image: UIImage?, status: String) -> ()) { println("Entering \(__FUNCTION__)") //Simulate a network operation by waiting a few seconds //before loading an image let nSecDispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(3.0 * Double(NSEC_PER_SEC))) let queue = dispatch_get_main_queue() dispatch_after(nSecDispatchTime, queue) { () -> Void in let result = UIImage(named: imageName) println("Loading image in background") let status = result != nil ? "image loaded" : "Error loading image" println("About to call completion handler") completion(image: result, status: status) } println("Leaving \(__FUNCTION__)") } 

它需要一个文件名和一个完成块。 完成块传递UIImage可选和String状态消息。

下载完成后,该方法将调用完成块(也称为闭包)。

以下是调用此方法的代码:

  @IBAction func loadImage(sender: UIButton) { let theAsyncManager = AsyncManager.sharedAsyncManager println("about to call asyncFetchImage") theAsyncManager.asyncFetchImage(imageName: "wareto_blue_150x115") { (image, status) -> () in println("Beginning completion block") self.theImageView.image = image println("In completion block, status = \(status)") } println("After call to asyncFetchImage") } 

println语句的输出是理解正在发生的事情的关键:

 about to call asyncFetchImage Entering asyncFetchImage(imageName:completion:) Leaving asyncFetchImage(imageName:completion:) After call to asyncFetchImage Loading image in background About to call completion handler Beginning completion block In completion block, status = image loaded 

请注意,在“在后台加载图像”消息之前打印“Leaving asyncFetchImage”和“After call to asyncFetchImage”消息。 然后是“关于调用完成处理程序”,然后是“开始完成块”。

因此,实际的异步工作甚至在loadImage函数返回之后才开始。

如果您不理解我所描述的内容,请下载项目并进行尝试,然后设置断点并观察它的执行情况。