在URLSession.shared.dataTask期间存储来自异步闭包的数据

时间:2017-05-26 16:41:24

标签: ios swift swift3 closures completionhandler

我尝试了两种技术来获取数据并从完成处理程序中填充数组。在这两种方法中,dataArray计数显示为0.然而,当执行在闭包内时,我能够放置断点并看到正在填充数组:

尝试的第一种方法:

在下面的代码中,dataArray显示的计数为零,即使在执行内部和外部completionHandler期间填充dataArray也是如此。

class ViewController: UIViewController { 
var dataArray = []
var urlOuter = URL(string: "outer.url.com/json")

override func viewDidLoad() {
    super.viewDidLoad()
    self.downloadTask()
    print(dataArray.count)
}

func downloadTask() {    

    let outerTask = URLSession.shared.dataTask(with: urlOuter!, completionHandler: {
    (data, response, error) in
    let parsedData = try JSONSerialization.jsonObject(with: content, options: .mutableContainers) as! [[String: Any]]

    for arr in parsedData! {
        var urlInner = URL(string: "http://inner.url/" + arr["url"] + ".com/json")
        let innerTask = URLSession.shared.dataTask(with: urlInner!, completionHandler: {
        (data, response, error) in
        let innerParsedData = try JSONSerialization.jsonObject(with: content, options: .mutableContainers) as! [[String: Any]]
        self.dataArray.append(innerParsedData)

        })
        innerTask.resume()   
    }// end of for loop

    })
    outerTask.resume()
}

}

尝试第二种方法:

protocol MyDelegate{ func didFetchData(data:String)}
class ViewController: UIViewController { 
var dataArray = []
var urlOuter = URL(string: "outer.url.com/json")

override func viewDidLoad() {
    super.viewDidLoad()
    self.downloadTask()
    print(dataArray.count)
}

func didFetchData(data:String) {
    self.dataArray.append(data)
}

func downloadTask() {    

    let outerTask = URLSession.shared.dataTask(with: urlOuter!, completionHandler: {
    (data, response, error) in
    let parsedData = try JSONSerialization.jsonObject(with: content, options: .mutableContainers) as! [[String: Any]]

    for arr in parsedData! {
        var urlInner = URL(string: "http://inner.url/" + arr["url"] + ".com/json")
        let innerTask = URLSession.shared.dataTask(with: urlInner!, completionHandler: {
        (data, response, error) in
        let innerParsedData = try JSONSerialization.jsonObject(with: content, options: .mutableContainers) as! String
        self. didFetchData(data:innerParsedData)

        })
        innerTask.resume()   
    }// end of for loop

    })
    outerTask.resume()
}}}

请帮助我了解如何从闭包中获取数据并将其存储在数组中。建议的其他解决方案是使用代理,这是我在方法2中尝试的。谢谢。

2 个答案:

答案 0 :(得分:0)

在调用以异步方法填充数据后,您正在使用viewDidLoad方法查询数组。

使用didFetchData()秒方法检查结果。

override func viewDidLoad() {
    super.viewDidLoad()
    self.downloadTask()
}


func didFetchData(data:String) {
    self.dataArray.append(data)
    // Check the count here!!
    print(dataArray.count)
}

您需要将协议更改为:

protocol MyDelegate{ func didFetchData(dataArray: [])}

然后为委托添加变量:

var mDelegate = MyDelegate?

然后分配你的结果:

func didFetchDataCompeleted(dataArray: []) {
    // hand over the data to the delegate
    mDelegate?.didFetchData(self.dataArray)
}

现在在闭包代码中完成innerTask后更改调用

self.didFetchDataCompeleted(dataArray:self.dataArray)

或只是致电:

self.mDelegate?.didFetchData(self.dataArray)

innerTask完成后

答案 1 :(得分:0)

我没有仔细观察,但你似乎正确地附加到数组。你出错的地方是要求过早计算。 URL请求是异步运行的,从CPU的角度来看需要很长时间:

self.downloadTask()     // this function run async
print(dataArray.count)  // nothing has been downloaded yet

试试这个:

func downloadTask(completionHandler: () -> Void) {      
    let outerTask = URLSession.shared.dataTask(with: urlOuter!) { data, response, error in
        let parsedData = try JSONSerialization.jsonObject(with: content, options: .mutableContainers) as! [[String: Any]]
        let group = DispatchGroup()

        for arr in parsedData! {
            var urlInner = URL(string: "http://inner.url/" + arr["url"] + ".com/json")
            group.enter()

            let innerTask = URLSession.shared.dataTask(with: urlInner!) { data, response, error in
                let innerParsedData = try JSONSerialization.jsonObject(with: content, options: .mutableContainers) as! [[String: Any]]

                // Appending to an array concurrently from multiple queues can lead to
                // weird error. The main queue is serial, which make sure that the
                // array is appended to once at a time
                DispatchQueue.main.async {
                    self.dataArray.append(innerParsedData)
                }
                group.leave()
            }
            innerTask.resume()
        }// end of for loop

        // Make sure all your inner tasks have completed
        group.wait(timeout: .forever)
        completionHandler()         
    }
    outerTask.resume()
}

override func viewDidLoad() {
    super.viewDidLoad()
    self.downloadTask() {
        print(dataArray.count)
    }
}