首页 > 解决方案 > Swift:异步 dataTask 永远不会终止

问题描述

我有一个函数可以抓取嵌入在网站中的 JSON 文件并返回一个字符串数组。因为它返回的数据对我的视图控制器来说是必不可少的,所以我需要在视图加载之前运行这个函数。它在视图控制器的初始化中被调用。

func scrapeBuses() -> [String] {
    let config = URLSessionConfiguration.default
    //config.waitsForConnectivity = true
    let defaultSession = URLSession(configuration: config)
    let url = URL(string: link to a JSON file)
    let request = NSMutableURLRequest(url: url!)
    request.cachePolicy = .reloadIgnoringLocalCacheData
    var loops = [String]()
    let group = DispatchGroup()
    group.enter()
    DispatchQueue.main.async {
        let task = defaultSession.dataTask(with: request as URLRequest) { data, response, error in
            do {
                print("Getting information from website")
                if let error = error {
                    print(error.localizedDescription)
                } else if let data = data, let response = response as? HTTPURLResponse, response.statusCode == 200 {
                    print("in async")
                    loops.append("test2")
                }
            }
            catch { print(error)}
        }
    }
    task.resume()
    print("about to return")
    //return loops
    }
    group.wait()
    return loops
}

因为它是异步的,所以我添加了DispatchGroupwait()语句,以便我的主线程等到这些基本数据被抓取后再继续线程的其余部分。然而,我注意到当这个视图控制器的 segue 发生时(即,当视图控制器被初始化时)没有任何其他内容被打印并且模拟停止。显然这意味着主线程正在等待scrape()完成,但是为什么它会无限运行呢?当我在我的网络浏览器和 Rested 中访问该网站时,我可以看到它正确地托管了 JSON。

编辑:

我使用完成处理程序而不是DispatchQueue.wait(). 下面是代码:

func scrapeBuses(completion: @escaping ([String]) -> Void) {
    let config = URLSessionConfiguration.default
    let defaultSession = URLSession(configuration: config)
    let url = URL(string: "https://www.cmunc.net/assets/appData.json")
    let request = NSMutableURLRequest(url: url!)
    request.cachePolicy = .reloadIgnoringLocalCacheData
    var loops = [String]()

        let task = defaultSession.dataTask(with: request as URLRequest) { data, response, error in
            print("Getting information from website")
            if let error = error {
                print(error.localizedDescription)
            } else if let data = data, let response = response as? HTTPURLResponse, response.statusCode == 200 {
                print("in async")
                loops.append("test2")
            }
            completion(loops)
        }
        task.resume()
}

这是视图控制器调用的函数init

func refresh() {
    var newArray = [String]()
    scrapeBuses { loops in
        newArray = loops
        print("calling scrapeBuses closure")
    }
    print(newArray)
}

使用完成处理程序,代码可以正确执行。中的打印语句scrapeBuses和闭幕中的打印语句。非常感谢所有提供见解的人。

标签: iosswiftasynchronousnsurlsession

解决方案


您应该将完成处理程序添加到您的scrapeBusesfunc。

func scrapeBuses(completion: ([String]?, Error?) -> Void) {
    // etc (remove all mentions of groups)

    let task = defaultSession.dataTask(with: request as URLRequest) { data, response, error in
        do {
            print("Getting information from website")
            if let error = error {
                DispatchQueue.main.async { // Call back on the main queue
                    completion(nil, error)
                }
            } else if let data = data, let response = response as? HTTPURLResponse, response.statusCode == 200 {
                print("in async")
                loops.append("test2")
            }
            DispatchQueue.main.async {
                completion(loops, nil)
            }
        } 
        catch { 
            DispatchQueue.main.async {
                completion(nil, error)
            }
        }
    }
    task.resume()
}

那么……</p>

func viewDidLoad() {
    super.viewDidLoad() 

    scrapeBuses { loops, error in
        if let error = error {    
            print(error)
            return
        }
        // Do something with loops
    }
}

推荐阅读