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

Posted

技术标签:

【中文标题】在 URLSession.shared.dataTask 期间存储来自异步闭包的数据【英文标题】:Storing data from an asynchronous closure during URLSession.shared.dataTask 【发布时间】:2017-05-26 16:41:24 【问题描述】:

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

尝试的第一种方法:

在下面的代码中,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()

尝试了第二种方法:

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 中尝试的方法。谢谢。

【问题讨论】:

我没有遵循所有的代码,但很有可能您将数据正确放入数组中。问题是您试图过早地打印计数。在append 之后立即打印您的结果。 谢谢@PhillipMills,当我在追加后调用 count 时,dataArray 正在递增。但是我需要在其他地方使用这些数据并且需要它是持久的。从外部访问此数组时,它显示为一个空数组。 【参考方案1】:

您在调用 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 完成时

【讨论】:

谢谢@Barns52 我尝试在 didFetchData 方法中检查计数,它显示了正确的计数。每次调用该方法时它都会递增。但是,我想在这个获取部分之外使用这个数组。有没有办法让数据持久化?谢谢! @asdiu 数据在 dataArray 中的持久性,但仅在加载之后。您应该能够通过在didFetchData 内部进行工作来“从外部”访问它,就像在那里工作的print 一样。另一种选择是在数组有效时设置一个标志,并在尝试使用数据之前让其他代码检查数据是否准备就绪。 我更改了答案以显示如何通过您的代表提供数据【参考方案2】:

我没有仔细看,但您似乎正确地附加到数组中。你出错的地方是过早地要求计数。 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)
    

【讨论】:

以上是关于在 URLSession.shared.dataTask 期间存储来自异步闭包的数据的主要内容,如果未能解决你的问题,请参考以下文章

分配的变量引用在哪里,在堆栈中还是在堆中?

NOIP 2015 & SDOI 2016 Round1 & CTSC 2016 & SDOI2016 Round2游记

秋的潇洒在啥?在啥在啥?

上传的数据在云端的怎么查看,保存在啥位置?

在 React 应用程序中在哪里转换数据 - 在 Express 中还是在前端使用 React?

存储在 plist 中的数据在模拟器中有效,但在设备中无效