Swift: 异步 dataTask 永不终止

Swift: Asynchronous dataTask never terminates

我有一个函数可以抓取网站中嵌入的 JSON 文件和 returns 字符串数组。由于它 returns 的数据对于我的视图控制器是必不可少的,因此我需要在视图加载之前将此函数 运行 。它在视图控制器的初始化中被调用。

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 中的打印语句和关闭打印中的打印语句。非常感谢所有提供见解的人。

因为你错过了

loops.append("test2")
group.leave()

或在回调之上更好

defer { group.leave() }

顺便说一句,最好有一个 activity 指标,而不是 wait

//

试试这个

func scrapeBuses(completion:@escaping(_ res:[String]?) -> Void ) {
    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 task = defaultSession.dataTask(with: request as URLRequest) { data, response, error in
            do {
                print("Getting information from website")
                if let error = error {
                    print(error.localizedDescription)
                     completion(nil)
                } else if let data = data, let response = response as? HTTPURLResponse, response.statusCode == 200 {
                    print("in async")
                    loops.append("test2")
                    completion(loops)
                }
            }
            catch { print(error)
                     completion(nil)
                  }
        }
    }
    task.resume()

}

您应该向 scrapeBuses 函数添加完成处理程序。

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

然后……

func viewDidLoad() {
    super.viewDidLoad() 

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