【问题标题】:Waiting for the result of a network request before assigning it to a variable in Swift在将网络请求分配给 Swift 中的变量之前等待网络请求的结果
【发布时间】:2019-11-26 15:24:55
【问题描述】:

我认为这个问题已经出现了很多次,但我无法理解所有 GCD 和完成处理的东西。非常感谢您提供一些指向有用文章的链接的快速修复。

我有一个从网络请求中获取和处理数据的函数:

  func getTracklist(album id: String) -> String {

        //create a GET request

        let task = URLSession.shared.dataTask(with: request) { (data, response, error) -> Void in
            guard let data = data else { return }
            let decoder = JSONDecoder()
            if let jsonTracks = try? decoder.decode(TrackRoot.self, from: data) {
                tracks = jsonTracks.items!
            }
            //append the tracklist string
            for track in tracks {
                combinedTracks += "\(track.id)%2C"
            }
        }
            task.resume()

        return String(combinedTracks.dropLast(3))

    }

返回的值在另一个函数中使用:

formattedAlbum.trackList = self.getTracklist(album: album.id!)

你可能已经猜到了,第一个函数没能及时获取数据,给tracklist分配了一个空字符串。

使用 Alexey 的回答:

for album in self.albums {
                let formattedAlbum = AlbumFormatted(context: self.persistenceManager.context)

                formattedAlbum.albumName = album.name
                self.getTracklist(album: album.id!) { (data) in
                    formattedAlbum.trackList = data
                }
                self.formattedAlbums.append(formattedAlbum)
                print(formattedAlbum)
            }
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(5)) {
                print(self.formattedAlbums[1])
            }
func getTracklist(album id: String, completion: @escaping (String?)->()) {
   //URLSession stuff

   let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
            guard let data = data else { return }
            let decoder = JSONDecoder()
            if let jsonTracks = try? decoder.decode(TrackRoot.self, from: data) {
                tracks = jsonTracks.items!
                for track in tracks {
                    combinedTracks += "\(track.id),"
                }
                completion(String(combinedTracks.dropLast()))
            }
        }
            task.resume()
    }

打印:

(entity: AlbumFormatted; id: 0x6000018fb400 <x-coredata:///AlbumFormatted/tFD0D9588-2820-451B-9A00-99175026ED043>; data: {
    albumName = "Everyday Life";
    trackList = "6Tb7Zfo4PcSiS4TqQ4NnTh,1e8D1BCD2afT56Km7UahpB,45PqOIkZ9PdCjsCJQYzx9G,1cXXhzPnbrXjNQYbLdUJdy,3pcPPhPAiurm2Ior11SHrz,7jib2tJjQ82kTIZZATMvAK,0ZlVUhjO8c0bOx1D2Btznf,0UvUivL70eDwhTWBd8S38I,6VzRvCbolqcUswaS";
})

【问题讨论】:

  • 你怀疑这是一个骗子,但要知道要骗哪个,这个方法的调用者是主队列上的(即它是 UI 代码的一部分)吗?如果是,那么你不能这样做(你需要一种不同的方法)。如果不是,那么有一种技术。最简单的骗子搜索在这里:stackoverflow.com/search?q=%5Bswift%5D+async+return
  • @RobNapier,这都是用来填充模型的,所以不涉及 UI 的东西。感谢您的链接,我会继续搜索。
  • 问题是dataTask(with:completionHandler:) 是一个异步函数,意思是该函数立即返回,但是一旦收到来自服务器的响应,完成处理程序将在稍后的时间点执行。最好理解异步函数以及为什么需要完成处理程序。在您的函数中,getTracklist 需要一个完成处理程序作为参数。
  • 澄清:1.jsonTracks 是否包含正确解码的json? 2.tracks打印出来了吗?这是否包含正确的参数? 3. combinedTracks 是否包含想要的结果?
  • @AlekseyPotapov,是的,最后一个代码 sn-p 中的 print(data) 语句正确打印出曲目,但没有为 formattedAlbum.trackList 分配任何内容。

标签: ios swift networking grand-central-dispatch


【解决方案1】:

我看到了使用闭包的解决方案: 只需在您的 ViewController 中测试以供参考。

override func viewDidLoad() {
    super.viewDidLoad()
    getTracklist { data in
        print(data)
    }
}

func getTracklist(completion: @escaping (Data?)->()) {
    let request = URLRequest(url: URL(string: "https://jsonplaceholder.typicode.com/todos/1")!)
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        // process errors here
        completion(data) // here you return your decoded data (I omit your json, as we don't have a model)
    }
    task.resume()
}

UPD 阅读您的 cmets 后:

for album in self.albums { 
    let formattedAlbum = AlbumFormatted(context: self.persistenceManager.context) 
    formattedAlbum.albumName = album.name 
    self.getTracklist(album: album.id!) { data in 
        print(data) 
        formattedAlbum.trackList = data 
    }
    print(formattedAlbum.trackList) // will return you nil
    // here you will not have results in formattedAlbum.trackList, because getTracklist method processes the data asynchronously   
}

检查thisthatthis SO 线程。

【讨论】:

  • 嗨!感谢你的回答。我尝试实现它,结果没有改变。更新了我的问题。我做错了吗?
  • 感谢您的宝贵时间,我会深入研究的!
  • 我尝试在几秒钟的最后期限后从 formattedAlbums 打印出专辑,并且它在“trackList”中包含正确的值。谢谢你让我明白! ?
  • @DanilKurilo 截止日期...你能展示你的工作解决方案吗?因为在执行异步操作时使用截止日期和计时器是不好的做法......
  • @Rob 是的,我完全同意你的看法。我会编辑答案,即使它已经被批准并且看起来很有用。
【解决方案2】:

这是处理回调的一种合法方式:

func getTrackList(album id: String, completion: @escaping(String)->()){
    //create a GET request
    let task = URLSession.shared.dataTask(with: request) { (data, response, error) -> Void in
        guard let data = data else { return }
        let decoder = JSONDecoder()
        if let jsonTracks = try? decoder.decode(TrackRoot.self, from: data) {
            tracks = jsonTracks.items!
        }
        //append the tracklist string
        for track in tracks {
            combinedTracks += "\(track.id)%2C"
        }
        completion(combinedTracks)
    }
    task.resume()
}

调用此函数时:

getTrackList(album: album.id) { [weak self] combinedTrack in
    guard let self = self else {return}
    self.formattedAlbum.trackList = combinedTrack
}

注意:如果请求失败或收到空响应/字符串,您可能还需要处理这些情况

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-23
    • 1970-01-01
    • 2020-01-06
    相关资源
    最近更新 更多