Swift:异步dataTask永不终止

时间:2018-09-03 19:18:36

标签: ios swift asynchronous nsurlsession

我有一个功能,可抓取嵌入网站的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()语句,以便我的主线程在继续其余线程之前一直等待直到必要的数据被擦除为止。但是,我注意到,当对该视图控制器进行锁定时(即,在初始化视图控制器时),没有其他内容被打印,并且模拟停止。显然,这意味着主线程正在等待scrape()完成,但是为什么它无限运行?当我在Web浏览器和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中的打印语句和封闭式打印中的打印语句。非常感谢每个提供了见解的人。

2 个答案:

答案 0 :(得分:1)

您应该将完成处理程序添加到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
    }
}

答案 1 :(得分:0)

因为你错过了

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

或者最好在回调之上

defer { group.leave() }

顺便说一句,最好有一个活动指示器,而不是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()

}