iOS Swift 如何访问在完成处理程序闭包中创建的数据——在闭包之外

Posted

技术标签:

【中文标题】iOS Swift 如何访问在完成处理程序闭包中创建的数据——在闭包之外【英文标题】:iOS Swift Hw to acces data created in a completion handler closure -- outside of the closure 【发布时间】:2015-07-06 21:25:25 【问题描述】:

我有一些使用

创建 MapKit 方向的代码
directions.calculateDirectionsWithCompletionHandler((response, error) in

方法。

代码工作正常——在完成处理程序中,但我不知道如何访问完成处理程序之外的数据。我已经定义了在完成处理程序之外使用的变量。

我意识到完成处理程序是异步运行的,但我不知道该怎么做才能弥补这一点。

最后,呈现的代码在 Swift Playground 中,因为完成处理程序闭包在那里运行 - 在应用程序中,由于 SSL 错误,完成处理程序闭包永远不会运行。

这是我能做到的尽可能少的代码:

//: Playground - noun: a place where people can play

import UIKit
import MapKit
import CoreLocation
import Contacts
import XCPlayground

XCPSetExecutionShouldContinueIndefinitely()

var directionsArray = [String]()
var addressString = ""

// Identify the Source and Destination
let source = MKMapItem(placemark: MKPlacemark(
    coordinate: CLLocationCoordinate2DMake(32.2345760,-110.8444420), addressDictionary: nil))
let destination = MKMapItem(placemark: MKPlacemark(
    coordinate: CLLocationCoordinate2DMake(34.104908,-118.137903), addressDictionary: nil))
let theLocation = CLLocation(latitude: source.placemark.coordinate.latitude, longitude: source.placemark.coordinate.longitude)

let directionsRequest = MKDirectionsRequest()
directionsRequest.source = source
directionsRequest.destination = destination
directionsRequest.requestsAlternateRoutes = true

let directions = MKDirections(request: directionsRequest)

print("before: directions.calculateDirectionsWithCompletionHandler addressString: \(addressString)")
print("before: directions.calculateDirectionsWithCompletionHandler directionsArray: \(directionsArray)")
directions.calculateDirectionsWithCompletionHandler((response, error) in
    var selectedRoute = 1

    addressString += "\n source: 6922 E 1st St, 85710 Tucson, AZ United States"
    addressString += "\n Destination: 2014–2048 Huntington Dr, 91030 South Pasadena, CA United States"

    print("\nwithin: directions.calculateDirectionsWithCompletionHandler: \n")
    print("error: \(error)")
    print("response!.routes.count \(response!.routes.count)")
    print("response!.routes[selectedRoute] \(response!.routes[selectedRoute])")

    let theSteps = response?.routes[selectedRoute].steps.count as Int!

    print("theSteps: \(theSteps)")
    print("distance: \(response!.routes[selectedRoute].distance) meters") // time \(response?.routes.first?.time)")
    addressString += "\n distance: \(response!.routes[selectedRoute].distance) meters"
    let myRoute = response?.routes[selectedRoute]
    print("myRoute!.polyline.pointCount \(myRoute!.polyline.pointCount)")
    print("myRoute!.steps[0].polyline.points(): \(myRoute!.steps[0].polyline.points())")
    print("polyline.points \(myRoute!.polyline.points())")

    // Build an array of Route Step Coordinates for later inclusion in Directions
    var stepCoordinates = [CLLocationCoordinate2D]()
    for step in myRoute!.steps as [MKRouteStep] 
        let pointCount = step.polyline.pointCount
        var cArray = UnsafeMutablePointer<CLLocationCoordinate2D>.alloc(pointCount)
        step.polyline.getCoordinates(cArray, range: NSMakeRange(0, pointCount))

        for var c=0; c < pointCount; c++ 
            let coord = cArray[c]
            if c == 0 
               var theCoordinate = CLLocationCoordinate2DMake(coord.latitude, coord.longitude)
            stepCoordinates.append(theCoordinate)
            
        
        cArray.dealloc(pointCount)
    

    // get the Directions Steps including the latitidude / longitude from thearray of Step Coordinates
    for index in  0..<theSteps 
        let lat = stepCoordinates[index].latitude
        let lon = stepCoordinates[index].longitude
        directionsArray.append("\n steps[\(index)] \(lat),\(lon) - \(myRoute!.steps[index].distance) meters - \(myRoute!.steps[index].instructions)")
    

    print("\nwithin: directions.calculateDirectionsWithCompletionHandler: addressString: \(addressString)")
    print("\nwithin: directions.calculateDirectionsWithCompletionHandler: directionsArray: \(directionsArray)")
)

print("\nafter: directions.calculateDirectionsWithCompletionHandler: addressString: \(addressString)")
print("after: directions.calculateDirectionsWithCompletionHandler: directionsArray: \(directionsArray)\n")

这是控制台输出:

之前:directions.calculateDirectionsWithCompletionHandler 地址字符串:

before: directions.calculateDirectionsWithCompletionHandler directionsArray: []

after: directions.calculateDirectionsWithCompletionHandler: addressString: 
after: directions.calculateDirectionsWithCompletionHandler: directionsArray: []

2015-07-06 14:00:58.421 GetDirectionsBasic[9758:4735303] Failed to obtain sandbox extension for path=/var/folders/7l/cgpb6wr9489b1qhxl8y38hvm0000gn/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.ios_Simulator.GetDirectionsBasic-98692A18-CEBC-4354-8E6A-5B12BA647B9E/Library/Caches/com.apple.dt.playground.stub.iOS_Simulator.GetDirectionsBasic-98692A18-CEBC-4354-8E6A-5B12BA647B9E. Errno:1
2015-07-06 14:00:58.422 GetDirectionsBasic[9758:4735303] Failed to obtain sandbox extension for path=/var/folders/7l/cgpb6wr9489b1qhxl8y38hvm0000gn/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.iOS_Simulator.GetDirectionsBasic-98692A18-CEBC-4354-8E6A-5B12BA647B9E/Library/Caches/com.apple.dt.playground.stub.iOS_Simulator.GetDirectionsBasic-98692A18-CEBC-4354-8E6A-5B12BA647B9E. Errno:1

within: directions.calculateDirectionsWithCompletionHandler: 

error: nil
response!.routes.count 3
response!.routes[selectedRoute] <MKRoute: 0x7fd7e0549220>
theSteps: 15
distance: 821009.0 meters
myRoute!.polyline.pointCount 2743
myRoute!.steps[0].polyline.points(): 0x00007fd7e1073000
polyline.points 0x00007fd7e1073000

within: directions.calculateDirectionsWithCompletionHandler: addressString: 
 source: 6922 E 1st St, 85710 Tucson, AZ United States
 Destination: 2014–2048 Huntington Dr, 91030 South Pasadena, CA United States
 distance: 821009.0 meters

within: directions.calculateDirectionsWithCompletionHandler: directionsArray: [
 steps[0] 32.2345979232341,-110.844443056463 - 0.0 meters - Proceed to E 1st St, 
 steps[1] 32.2345979232341,-110.844443056463 - 105.0 meters - At the end of the road, turn left onto N Green Hills Ave, 
 steps[2] 32.2345979232341,-110.843325078218 - 117.0 meters - Turn left onto E Speedway Blvd, 
 steps[3] 32.2356559708714,-110.843304039641 - 13216.0 meters - Turn right onto N Freeway Rd toward Phoenix, 
 steps[4] 32.235831990838,-110.983517018841 - 132.0 meters - Keep left to merge onto I-10 W, 
 steps[5] 32.236999925226,-110.9837630277 - 92506.0 meters - Take exit 199 to merge onto I-8 W toward San Diego, 
 steps[6] 32.8114569280297,-111.674388011842 - 374340.0 meters - Take exit 118B onto CA-111 N toward Indio, 
 steps[7] 32.773857973516,-115.495294079998 - 33287.0 meters - Turn right onto State Highway 86, 
 steps[8] 33.0002589616925,-115.585204073131 - 107741.0 meters - Continue onto I-10 W, 
 steps[9] 33.7204659450799,-116.19523705826 - 184120.0 meters - Keep left, 
 steps[10] 34.0650959406048,-118.013762030288 - 11125.0 meters - Take exit 23A onto Atlantic Blvd, 
 steps[11] 34.0716939233243,-118.132452042339 - 103.0 meters - Keep right on S Atlantic Blvd, 
 steps[12] 34.0717909857631,-118.133564069433 - 3815.0 meters - Turn left onto Garfield Ave, 
 steps[13] 34.1057089436799,-118.134696045456 - 108.0 meters - Turn left onto Huntington Dr, 
 steps[14] 34.1065149474889,-118.13535704234 - 294.0 meters - The destination is on your left]

【问题讨论】:

完成块在异步函数之后调用,因此块之后的代码实际上在块之前执行(异步函数需要时间)。因此,addressString 和 DirectionArray 不会更新。 @ad121 我明白……但我不能成为第一个或唯一一个想要保存异步完成块中创建的数据以供以后使用的人。我什至找到了一个提议的解决方案,涉及 CGD 在异步完成块线程完成时触发主线程——这也不起作用。令我惊讶的是,所有使用 MapKit 的 iOS 活动——没有可用的 [工作] 示例代码——用于方向、地理编码......以及使用异步完成块的其他事情。沮丧——WWDC 2015 上所有这些巧妙的新地图功能——并且无法使用它们。 我会使用调度组和调度通知在所有操作之后收集更新的值。基本上 dg.enter(), dg.leave() - 当完成和 dg.notify 在所有执行离开后调用通知完成处理程序。 【参考方案1】:

你说得对,这里有一个关于异步的基本问题。它实际上与 MapKit 或您代码的大多数其他细节无关,所以我在下面的答案中将它们抽象出来。

“异步”意味着,对于任何看起来像这样的东西:

...绿色和蓝色块运行棕色块之前:

(此外,蓝色和棕色块之间的时间可以任意短或长,主线程在此期间更新 UI。)

这意味着:

蓝色块中的任何代码都不知道棕色块中发生的事情,因为它还没有发生。 (如果发生其他情况,你就违反了因果关系......通知斯蒂芬霍金。并提交雷达。)

由于棕色块中的代码,您希望发生的任何事情都需要从棕色块内触发。 (或者来自其他地方,可以保证“稍后”。)

这也意味着,如果您正在尝试以下模式:

func getDirections() -> String 
    var directions: [String]
    getMKDirectionsWithCompletionHandler()  result, error in 
        directions = // something from result
    
    return directions

...你有一个根本不可行的设计。您需要查看要调用 getDirections() 函数的位置并使用其结果,并将它们设计为异步的。

例如,如果你想做这样的事情:

let theDirections = getDirections()
destinationLabel.text = theDirections.last!

你需要做这样的事情:

getMKDirectionsWithCompletionHandler()  result, error in 
    let directions: [String] = // something from result
    destinationLabel.text = directions.last! // use it *in* the completion block

或者,还记得我说过“以后肯定会在其他地方”吗?假设您想在表格视图中显示这些字符串:

class MyViewController: UITableViewController 
    var directions = [String]()

    func viewWillAppear() 
        getMKDirectionsWithCompletionHandler()  result, error in 
            directions = // something from result

            // make sure the UI updates after we have our result
            self.tableView.reloadData() 
        
    

    func tableView(tv: UITableView, numberOfRowsInSection section: Int) -> Int 
        return directions.count
    

    func tableView(tv: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 
        let directionsItem = directions[indexPath.row]
        return // cell constructed from directionsItem
    

【讨论】:

感谢您的回复!我已经研究了几个星期,没有成功。我尝试了你的两种方法都没有成功......如果你可以发布一个工作代码 sn-p (希望是一个游乐场),那真的很有帮助。提前致谢。【参考方案2】:

借助@matt 的延迟例程和@gooroo7 的文件读/写例程,这是 Playground 中的一个工作示例...

由于在发起获取路线请求时出现 SSL 握手问题,我无法让它在应用中运行:

mapDirections.calculateDirectionsWithCompletionHandler((response, error) in

我已经包含了相当冗长的代码和控制台输出。来自@matt 和@gooroo7 的例程记录在哪里使用。此外,控制台输出相当长——显示文件和使用的变量的之前、期间和之后(关闭)状态。

这是游乐场代码:

//: Playground - noun: a place where people can play

import UIKit
import MapKit
import CoreLocation
import Contacts
import XCPlayground

XCPSetExecutionShouldContinueIndefinitely()

var directionsArray = [String]()
var addressString = String()

func setAddressString(theString:String)
    addressString = theString
    print("setAddressString \(addressString)")



// http://***.com/questions/24097826/read-and-write-data-from-text-file
// answer by goroo7
//To avoid confusion and add ease, I have created two functions for reading and writing strings to files in the documents directory. Here are the functions:
//--------------------------------------------------------------------------------------------------------------
func writeToDocumentsFile(fileName:String,value:String) 
    let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] // as! NSString
    let path = documentsPath.stringByAppendingPathComponent(fileName)
    //    var error:NSError?
    do 
        try value.writeToFile(path, atomically: true, encoding: NSUTF8StringEncoding) //, error: nil)
    
    catch let error as NSError 
        // Catch fires here, with an NSErrror being thrown from the value.writeToFile method
        print("A write to file error occurred, here are the details:\n \(error)")
    


func readFromDocumentsFile(fileName:String) -> String 
    let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] //as! NSString
    let path = documentsPath.stringByAppendingPathComponent(fileName)
    let checkValidation = NSFileManager.defaultManager()
    //    var error:NSError?
    //    var file:String

    var file = ""
    if checkValidation.fileExistsAtPath(path) 
        do 
            try file = NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding) as String
        
        catch let error as NSError 
            // Catch fires here, with an NSErrror being thrown from the JSONObjectWithData method
            print("A read from file error occurred, here are the details:\n \(error)")
        

     else 
        file = "*ERROR* \(fileName) does not exist."
    
    print("return file: \(file)")
    return file

//--------------------------------------------------------------------------------------------------------------


func getDirections() 
    print("getDirections")
    // Identify the Source and Destination
    let source = MKMapItem(placemark: MKPlacemark(
        coordinate: CLLocationCoordinate2DMake(32.2345760,-110.8444420), addressDictionary: nil))
    let destination = MKMapItem(placemark: MKPlacemark(
        coordinate: CLLocationCoordinate2DMake(34.104908,-118.137903), addressDictionary: nil))
    let theLocation = CLLocation(latitude: source.placemark.coordinate.latitude, longitude: source.placemark.coordinate.longitude)

    let directionsRequest = MKDirectionsRequest()
    directionsRequest.source = source
    directionsRequest.destination = destination
    directionsRequest.requestsAlternateRoutes = true

    let mapDirections = MKDirections(request: directionsRequest)

    print("before: directions.calculateDirectionsWithCompletionHandler addressString: \(addressString)")
    print("before: directions.calculateDirectionsWithCompletionHandler directionsArray: \(directionsArray)\n")
    mapDirections.calculateDirectionsWithCompletionHandler((response, error) in
        let selectedRoute = 1

        let todaysDate:NSDate = NSDate()
        let dateFormatter:NSDateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "MM-dd-yyyy HH:mm"
        let DateInFormat:String = dateFormatter.stringFromDate(todaysDate)
        //print("\nDateInFormat: \(DateInFormat)\n")

        addressString = "\n..........addressString" //\n \(DateInFormat)"
        addressString += ("\n Date \(DateInFormat)")
        addressString += ("\n source: 6922 E 1st St, 85710 Tucson, AZ United States")
        addressString += ("\n Destination: 2014–2048 Huntington Dr, 91030 South Pasadena, CA United States")

        print("\nwithin: directions.calculateDirectionsWithCompletionHandler: \n")
        print("error: \(error)")
        print("response!.routes.count \(response!.routes.count)")
        print("response!.routes[selectedRoute] \(response!.routes[selectedRoute])")

        let theSteps = response?.routes[selectedRoute].steps.count as Int!

        print("theSteps: \(theSteps)")
        print("distance: \(response!.routes[selectedRoute].distance) meters") // time \(response?.routes.first?.time)")
        addressString += ("\n distance: \(response!.routes[selectedRoute].distance) meters")
        let myRoute = response?.routes[selectedRoute]
        print("myRoute!.polyline.pointCount \(myRoute!.polyline.pointCount)")
        print("myRoute!.steps[0].polyline.points(): \(myRoute!.steps[0].polyline.points())")
        print("polyline.points \(myRoute!.polyline.points())")

        // Build an array of Route Step Coordinates for later inclusion in Directions
        var stepCoordinates = [CLLocationCoordinate2D]()
        for step in myRoute!.steps as [MKRouteStep] 
            let pointCount = step.polyline.pointCount
            var cArray = UnsafeMutablePointer<CLLocationCoordinate2D>.alloc(pointCount)
            step.polyline.getCoordinates(cArray, range: NSMakeRange(0, pointCount))

            for var c=0; c < pointCount; c++ 
                let coord = cArray[c]
                if c == 0 
                   var theCoordinate = CLLocationCoordinate2DMake(coord.latitude, coord.longitude)
                stepCoordinates.append(theCoordinate)
                
            
            cArray.dealloc(pointCount)
        

        // get the Directions Steps including the latitidude / longitude from thearray of Step Coordinates
        for index in  0..<theSteps 
            let lat = stepCoordinates[index].latitude
            let lon = stepCoordinates[index].longitude
            directionsArray.append("\n steps[\(index)] \(lat),\(lon) - \(myRoute!.steps[index].distance) meters - \(myRoute!.steps[index].instructions)")
            addressString += ("\n steps[\(index)] \(lat),\(lon) - \(myRoute!.steps[index].distance) meters - \(myRoute!.steps[index].instructions)")

        

        //        let directions: [String] = addressString
        setAddressString(addressString)
        print("\nwithin: directions.calculateDirectionsWithCompletionHandler: addressString: \(addressString)")
        print("\nwithin: directions.calculateDirectionsWithCompletionHandler: directionsArray: \(directionsArray)")


        // http://***.com/questions/24097826/read-and-write-data-from-text-file
        // answer by goroo7
        //To avoid confusion and add ease, I have created two functions for reading and writing strings to files in the documents directory. Here are the functions: ... Here is an example of their use:
        //--------------------------------------------------------------------------------------------------------------
        writeToDocumentsFile("addressString.txt",value: addressString)
        //--------------------------------------------------------------------------------------------------------------

    )

getDirections()

// http://***.com/questions/24034544/dispatch-after-gcd-in-swift/24318861#24318861
// answer by @matt
// I use dispatch_after so often that I wrote a top-level utility function to make the syntax simpler:
//--------------------------------------------------------------------------------------------------------------
func delay(delay:Double, closure:()->()) 
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)


//And now you can talk like this:

delay(3.0) 
    // do stuff
    let value = readFromDocumentsFile("addressString.txt")
    print("\nAfter Delay - readFromDocumentsFile written in closure: \(value)\n")  //Would output 'Hello world!'
    print("\nAfter Delay - read from external var addressString updated in closure: \(addressString)")
    print("\nAfter Delay - read from external var directionsArray updated in closure:\(directionsArray)")

//--------------------------------------------------------------------------------------------------------------

这是控制台输出:

getDirections
before: directions.calculateDirectionsWithCompletionHandler addressString: 
before: directions.calculateDirectionsWithCompletionHandler directionsArray: []

2015-07-07 16:12:31.116 GetDirectionsBasic[1768:630471] Failed to obtain sandbox extension for path=/var/folders/7l/cgpb6wr9489b1qhxl8y38hvm0000gn/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.iOS_Simulator.GetDirectionsBasic-9B1F8F3C-4BBC-430F-AEFE-469C81A24CBE/Library/Caches/com.apple.dt.playground.stub.iOS_Simulator.GetDirectionsBasic-9B1F8F3C-4BBC-430F-AEFE-469C81A24CBE. Errno:1
2015-07-07 16:12:31.116 GetDirectionsBasic[1768:630471] Failed to obtain sandbox extension for path=/var/folders/7l/cgpb6wr9489b1qhxl8y38hvm0000gn/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.iOS_Simulator.GetDirectionsBasic-9B1F8F3C-4BBC-430F-AEFE-469C81A24CBE/Library/Caches/com.apple.dt.playground.stub.iOS_Simulator.GetDirectionsBasic-9B1F8F3C-4BBC-430F-AEFE-469C81A24CBE. Errno:1

within: directions.calculateDirectionsWithCompletionHandler: 

error: nil
response!.routes.count 3
response!.routes[selectedRoute] <MKRoute: 0x7fed8a212e60>
theSteps: 15
distance: 821009.0 meters
myRoute!.polyline.pointCount 2743
myRoute!.steps[0].polyline.points(): 0x00007fed89052a00
polyline.points 0x00007fed89052a00
setAddressString 
..........addressString
 Date 07-07-2015 16:12
 source: 6922 E 1st St, 85710 Tucson, AZ United States
 Destination: 2014–2048 Huntington Dr, 91030 South Pasadena, CA United States
 distance: 821009.0 meters
 steps[0] 32.2345979232341,-110.844443056463 - 0.0 meters - Proceed to E 1st St
 steps[1] 32.2345979232341,-110.844443056463 - 105.0 meters - At the end of the road, turn left onto N Green Hills Ave
 steps[2] 32.2345979232341,-110.843325078218 - 117.0 meters - Turn left onto E Speedway Blvd
 steps[3] 32.2356559708714,-110.843304039641 - 13216.0 meters - Turn right onto N Freeway Rd toward Phoenix
 steps[4] 32.235831990838,-110.983517018841 - 132.0 meters - Keep left to merge onto I-10 W
 steps[5] 32.236999925226,-110.9837630277 - 92506.0 meters - Take exit 199 to merge onto I-8 W toward San Diego
 steps[6] 32.8114569280297,-111.674388011842 - 374340.0 meters - Take exit 118B onto CA-111 N toward Indio
 steps[7] 32.773857973516,-115.495294079998 - 33287.0 meters - Turn right onto State Highway 86
 steps[8] 33.0002589616925,-115.585204073131 - 107741.0 meters - Continue onto I-10 W
 steps[9] 33.7204659450799,-116.19523705826 - 184120.0 meters - Keep left
 steps[10] 34.0650959406048,-118.013762030288 - 11125.0 meters - Take exit 23A onto Atlantic Blvd
 steps[11] 34.0716939233243,-118.132452042339 - 103.0 meters - Keep right on S Atlantic Blvd
 steps[12] 34.0717909857631,-118.133564069433 - 3815.0 meters - Turn left onto Garfield Ave
 steps[13] 34.1057089436799,-118.134696045456 - 108.0 meters - Turn left onto Huntington Dr
 steps[14] 34.1065149474889,-118.13535704234 - 294.0 meters - The destination is on your left

within: directions.calculateDirectionsWithCompletionHandler: addressString: 
..........addressString
 Date 07-07-2015 16:12
 source: 6922 E 1st St, 85710 Tucson, AZ United States
 Destination: 2014–2048 Huntington Dr, 91030 South Pasadena, CA United States
 distance: 821009.0 meters
 steps[0] 32.2345979232341,-110.844443056463 - 0.0 meters - Proceed to E 1st St
 steps[1] 32.2345979232341,-110.844443056463 - 105.0 meters - At the end of the road, turn left onto N Green Hills Ave
 steps[2] 32.2345979232341,-110.843325078218 - 117.0 meters - Turn left onto E Speedway Blvd
 steps[3] 32.2356559708714,-110.843304039641 - 13216.0 meters - Turn right onto N Freeway Rd toward Phoenix
 steps[4] 32.235831990838,-110.983517018841 - 132.0 meters - Keep left to merge onto I-10 W
 steps[5] 32.236999925226,-110.9837630277 - 92506.0 meters - Take exit 199 to merge onto I-8 W toward San Diego
 steps[6] 32.8114569280297,-111.674388011842 - 374340.0 meters - Take exit 118B onto CA-111 N toward Indio
 steps[7] 32.773857973516,-115.495294079998 - 33287.0 meters - Turn right onto State Highway 86
 steps[8] 33.0002589616925,-115.585204073131 - 107741.0 meters - Continue onto I-10 W
 steps[9] 33.7204659450799,-116.19523705826 - 184120.0 meters - Keep left
 steps[10] 34.0650959406048,-118.013762030288 - 11125.0 meters - Take exit 23A onto Atlantic Blvd
 steps[11] 34.0716939233243,-118.132452042339 - 103.0 meters - Keep right on S Atlantic Blvd
 steps[12] 34.0717909857631,-118.133564069433 - 3815.0 meters - Turn left onto Garfield Ave
 steps[13] 34.1057089436799,-118.134696045456 - 108.0 meters - Turn left onto Huntington Dr
 steps[14] 34.1065149474889,-118.13535704234 - 294.0 meters - The destination is on your left

within: directions.calculateDirectionsWithCompletionHandler: directionsArray: [
 steps[0] 32.2345979232341,-110.844443056463 - 0.0 meters - Proceed to E 1st St, 
 steps[1] 32.2345979232341,-110.844443056463 - 105.0 meters - At the end of the road, turn left onto N Green Hills Ave, 
 steps[2] 32.2345979232341,-110.843325078218 - 117.0 meters - Turn left onto E Speedway Blvd, 
 steps[3] 32.2356559708714,-110.843304039641 - 13216.0 meters - Turn right onto N Freeway Rd toward Phoenix, 
 steps[4] 32.235831990838,-110.983517018841 - 132.0 meters - Keep left to merge onto I-10 W, 
 steps[5] 32.236999925226,-110.9837630277 - 92506.0 meters - Take exit 199 to merge onto I-8 W toward San Diego, 
 steps[6] 32.8114569280297,-111.674388011842 - 374340.0 meters - Take exit 118B onto CA-111 N toward Indio, 
 steps[7] 32.773857973516,-115.495294079998 - 33287.0 meters - Turn right onto State Highway 86, 
 steps[8] 33.0002589616925,-115.585204073131 - 107741.0 meters - Continue onto I-10 W, 
 steps[9] 33.7204659450799,-116.19523705826 - 184120.0 meters - Keep left, 
 steps[10] 34.0650959406048,-118.013762030288 - 11125.0 meters - Take exit 23A onto Atlantic Blvd, 
 steps[11] 34.0716939233243,-118.132452042339 - 103.0 meters - Keep right on S Atlantic Blvd, 
 steps[12] 34.0717909857631,-118.133564069433 - 3815.0 meters - Turn left onto Garfield Ave, 
 steps[13] 34.1057089436799,-118.134696045456 - 108.0 meters - Turn left onto Huntington Dr, 
 steps[14] 34.1065149474889,-118.13535704234 - 294.0 meters - The destination is on your left]
return file: 
..........addressString
 Date 07-07-2015 16:12
 source: 6922 E 1st St, 85710 Tucson, AZ United States
 Destination: 2014–2048 Huntington Dr, 91030 South Pasadena, CA United States
 distance: 821009.0 meters
 steps[0] 32.2345979232341,-110.844443056463 - 0.0 meters - Proceed to E 1st St
 steps[1] 32.2345979232341,-110.844443056463 - 105.0 meters - At the end of the road, turn left onto N Green Hills Ave
 steps[2] 32.2345979232341,-110.843325078218 - 117.0 meters - Turn left onto E Speedway Blvd
 steps[3] 32.2356559708714,-110.843304039641 - 13216.0 meters - Turn right onto N Freeway Rd toward Phoenix
 steps[4] 32.235831990838,-110.983517018841 - 132.0 meters - Keep left to merge onto I-10 W
 steps[5] 32.236999925226,-110.9837630277 - 92506.0 meters - Take exit 199 to merge onto I-8 W toward San Diego
 steps[6] 32.8114569280297,-111.674388011842 - 374340.0 meters - Take exit 118B onto CA-111 N toward Indio
 steps[7] 32.773857973516,-115.495294079998 - 33287.0 meters - Turn right onto State Highway 86
 steps[8] 33.0002589616925,-115.585204073131 - 107741.0 meters - Continue onto I-10 W
 steps[9] 33.7204659450799,-116.19523705826 - 184120.0 meters - Keep left
 steps[10] 34.0650959406048,-118.013762030288 - 11125.0 meters - Take exit 23A onto Atlantic Blvd
 steps[11] 34.0716939233243,-118.132452042339 - 103.0 meters - Keep right on S Atlantic Blvd
 steps[12] 34.0717909857631,-118.133564069433 - 3815.0 meters - Turn left onto Garfield Ave
 steps[13] 34.1057089436799,-118.134696045456 - 108.0 meters - Turn left onto Huntington Dr
 steps[14] 34.1065149474889,-118.13535704234 - 294.0 meters - The destination is on your left

After Delay - readFromDocumentsFile written in closure: 
..........addressString
 Date 07-07-2015 16:12
 source: 6922 E 1st St, 85710 Tucson, AZ United States
 Destination: 2014–2048 Huntington Dr, 91030 South Pasadena, CA United States
 distance: 821009.0 meters
 steps[0] 32.2345979232341,-110.844443056463 - 0.0 meters - Proceed to E 1st St
 steps[1] 32.2345979232341,-110.844443056463 - 105.0 meters - At the end of the road, turn left onto N Green Hills Ave
 steps[2] 32.2345979232341,-110.843325078218 - 117.0 meters - Turn left onto E Speedway Blvd
 steps[3] 32.2356559708714,-110.843304039641 - 13216.0 meters - Turn right onto N Freeway Rd toward Phoenix
 steps[4] 32.235831990838,-110.983517018841 - 132.0 meters - Keep left to merge onto I-10 W
 steps[5] 32.236999925226,-110.9837630277 - 92506.0 meters - Take exit 199 to merge onto I-8 W toward San Diego
 steps[6] 32.8114569280297,-111.674388011842 - 374340.0 meters - Take exit 118B onto CA-111 N toward Indio
 steps[7] 32.773857973516,-115.495294079998 - 33287.0 meters - Turn right onto State Highway 86
 steps[8] 33.0002589616925,-115.585204073131 - 107741.0 meters - Continue onto I-10 W
 steps[9] 33.7204659450799,-116.19523705826 - 184120.0 meters - Keep left
 steps[10] 34.0650959406048,-118.013762030288 - 11125.0 meters - Take exit 23A onto Atlantic Blvd
 steps[11] 34.0716939233243,-118.132452042339 - 103.0 meters - Keep right on S Atlantic Blvd
 steps[12] 34.0717909857631,-118.133564069433 - 3815.0 meters - Turn left onto Garfield Ave
 steps[13] 34.1057089436799,-118.134696045456 - 108.0 meters - Turn left onto Huntington Dr
 steps[14] 34.1065149474889,-118.13535704234 - 294.0 meters - The destination is on your left


After Delay - read from external var addressString updated in closure: 
..........addressString
 Date 07-07-2015 16:12
 source: 6922 E 1st St, 85710 Tucson, AZ United States
 Destination: 2014–2048 Huntington Dr, 91030 South Pasadena, CA United States
 distance: 821009.0 meters
 steps[0] 32.2345979232341,-110.844443056463 - 0.0 meters - Proceed to E 1st St
 steps[1] 32.2345979232341,-110.844443056463 - 105.0 meters - At the end of the road, turn left onto N Green Hills Ave
 steps[2] 32.2345979232341,-110.843325078218 - 117.0 meters - Turn left onto E Speedway Blvd
 steps[3] 32.2356559708714,-110.843304039641 - 13216.0 meters - Turn right onto N Freeway Rd toward Phoenix
 steps[4] 32.235831990838,-110.983517018841 - 132.0 meters - Keep left to merge onto I-10 W
 steps[5] 32.236999925226,-110.9837630277 - 92506.0 meters - Take exit 199 to merge onto I-8 W toward San Diego
 steps[6] 32.8114569280297,-111.674388011842 - 374340.0 meters - Take exit 118B onto CA-111 N toward Indio
 steps[7] 32.773857973516,-115.495294079998 - 33287.0 meters - Turn right onto State Highway 86
 steps[8] 33.0002589616925,-115.585204073131 - 107741.0 meters - Continue onto I-10 W
 steps[9] 33.7204659450799,-116.19523705826 - 184120.0 meters - Keep left
 steps[10] 34.0650959406048,-118.013762030288 - 11125.0 meters - Take exit 23A onto Atlantic Blvd
 steps[11] 34.0716939233243,-118.132452042339 - 103.0 meters - Keep right on S Atlantic Blvd
 steps[12] 34.0717909857631,-118.133564069433 - 3815.0 meters - Turn left onto Garfield Ave
 steps[13] 34.1057089436799,-118.134696045456 - 108.0 meters - Turn left onto Huntington Dr
 steps[14] 34.1065149474889,-118.13535704234 - 294.0 meters - The destination is on your left

After Delay - read from external var directionsArray updated in closure:[
 steps[0] 32.2345979232341,-110.844443056463 - 0.0 meters - Proceed to E 1st St, 
 steps[1] 32.2345979232341,-110.844443056463 - 105.0 meters - At the end of the road, turn left onto N Green Hills Ave, 
 steps[2] 32.2345979232341,-110.843325078218 - 117.0 meters - Turn left onto E Speedway Blvd, 
 steps[3] 32.2356559708714,-110.843304039641 - 13216.0 meters - Turn right onto N Freeway Rd toward Phoenix, 
 steps[4] 32.235831990838,-110.983517018841 - 132.0 meters - Keep left to merge onto I-10 W, 
 steps[5] 32.236999925226,-110.9837630277 - 92506.0 meters - Take exit 199 to merge onto I-8 W toward San Diego, 
 steps[6] 32.8114569280297,-111.674388011842 - 374340.0 meters - Take exit 118B onto CA-111 N toward Indio, 
 steps[7] 32.773857973516,-115.495294079998 - 33287.0 meters - Turn right onto State Highway 86, 
 steps[8] 33.0002589616925,-115.585204073131 - 107741.0 meters - Continue onto I-10 W, 
 steps[9] 33.7204659450799,-116.19523705826 - 184120.0 meters - Keep left, 
 steps[10] 34.0650959406048,-118.013762030288 - 11125.0 meters - Take exit 23A onto Atlantic Blvd, 
 steps[11] 34.0716939233243,-118.132452042339 - 103.0 meters - Keep right on S Atlantic Blvd, 
 steps[12] 34.0717909857631,-118.133564069433 - 3815.0 meters - Turn left onto Garfield Ave, 
 steps[13] 34.1057089436799,-118.134696045456 - 108.0 meters - Turn left onto Huntington Dr, 
 steps[14] 34.1065149474889,-118.13535704234 - 294.0 meters - The destination is on your left]

感谢所有提供帮助的人——希望这对其他人有用!

【讨论】:

调度组将确保您想要的所有步骤在继续之前完成。然后,您可以将 UI 包装到主线程中,以便在完成后更新 UI 元素。这使得延迟调用变得不必要。天知道这些人为的延迟什么时候会开始给你带来神秘的错误。 (有关更多信息,请参阅文档)【参考方案3】:

由于完成处理程序在另一个线程上运行,因此 dispatch_async 到要在完成处理程序内部使用数据的线程。这样您就可以在数据实际到达后访问它。

【讨论】:

这不是我的问题。我想使用在完成处理程序中创建的数据——在完成处理程序之外。例如,我想创建一个外部 .gpx 文件以与 Apple 地图应用程序一起使用。

以上是关于iOS Swift 如何访问在完成处理程序闭包中创建的数据——在闭包之外的主要内容,如果未能解决你的问题,请参考以下文章

swift - 如何从系统函数的完成处理程序闭包中返回?

Swift 完成处理程序 - 转义尾随闭包

Swift 闭包完成处理程序

如何从 Swift 中的 void 闭包中返回一个值?

如何在 Swift 中创建一个我可以选择调用的完成处理程序?

Swift-使用完成处理程序更新闭包外的全局变量