简体   繁体   English

在URLSession.shared.dataTask期间从异步关闭存储数据

[英]Storing data from an asynchronous closure during URLSession.shared.dataTask

I have tried two techniques to get data and fill an array from completion handlers. 我尝试了两种技术来获取数据并从完成处理程序中填充数组。 In both methods, the dataArray count is showing as 0. Whereas I'm able to put breakpoints and see that the array is being populated when the execution is within the closure: 在这两种方法中,dataArray的计数都显示为0。而我能够放置断点,并在执行在闭包内时看到正在填充数组:

First Method Tried: 第一种方法尝试过:

In the below code, dataArray shows a count of zero even though it is populating the dataArray during execution of both inner and outer completionHandlers. 在下面的代码中,即使在内部和外部completionHandlers执行期间都在填充dataArray,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()
}

} }

Second Method Tried: 尝试的第二种方法:

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()
}}}

Please help me understand how to get data out of the closures and store them in the array. 请帮助我了解如何从闭包中获取数据并将其存储在数组中。 Other solutions suggested are to use delegates and that is what I tried in method 2. Thank you. 建议的其他解决方案是使用委托,这就是我在方法2中尝试过的方法。谢谢。

You are querying the array in the viewDidLoad method right after you call to populate it in a async method. 您在调用以异步方法填充数组后立即在viewDidLoad方法中查询该数组。

check the results in the didFetchData() second method. didFetchData()第二种方法中检查结果。

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


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

You will need to change your protocol to: 您将需要将协议更改为:

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

Then add the variable for the delegate: 然后为委托添加变量:

var mDelegate = MyDelegate?

Then assign your result: 然后分配结果:

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

now change the the call when the innerTask is completed within your closure code to 现在,在您的关闭代码中完成innerTask ,将调用更改为

self.didFetchDataCompeleted(dataArray:self.dataArray)

or just call: 或致电:

self.mDelegate?.didFetchData(self.dataArray)

when the innerTask is finished innerTask完成时

I haven't look to closely but you seemed to be appending to the array correctly. 我没有仔细查看,但是您似乎正确地附加到了数组。 Where you went wrong is asking for the count too soon. 您出问题的地方是过早要求计数。 URL requests are run asynchronously and takes ages from the CPU's perspective: URL请求是异步运行的,并且从CPU的角度来看需要很长时间:

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

try this: 尝试这个:

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)
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 URLSession.shared.dataTask的完成处理程序内部的闭包 - closure inside completion handler of URLSession.shared.dataTask URLSession.shared.dataTask接收数据的正确方法 - URLSession.shared.dataTask correct way to receive data 在URLSession.shared.dataTask和ViewController之间共享数据 - Sharing data between URLSession.shared.dataTask and ViewController iOS:来自 URLSession.shared.dataTask 的数据(带有:url 始终为零(在 xcode 调试器中显示 0 字节错误?) - iOS: Data from URLSession.shared.dataTask(with: url is always nil (display 0 bytes bug in xcode debugger?) 从 URLSession.shared.dataTask 获取当前进度 - Get Current Progress from URLSession.shared.dataTask 在URLSession.shared.dataTask之后调用performSegue - Call performSegue after URLSession.shared.dataTask URLSession.shared.dataTask 冻结 UI - URLSession.shared.dataTask freezes the UI 在URLSession.shared.dataTask中执行performSegueWithIdentifier(with:url) - performSegueWithIdentifier while in URLSession.shared.dataTask(with: url) 命中Node Express端点时URLSession.shared.dataTask数据始终为空 - URLSession.shared.dataTask Data is Always Empty When Hitting Node Express Endpoint 如何获取请求中标头的值(URLSession.shared.dataTask(with:request){(数据,响应,错误) - How get value of headers in request (URLSession.shared.dataTask(with: request) { (data, response, error)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM