【问题标题】:data with request completionhandler not always called具有请求完成处理程序的数据并不总是被调用
【发布时间】:2016-10-03 13:12:42
【问题描述】:

我正在制作一个应用程序,您可以在其中使用 http://www.omdbapi.com/ 的 API 搜索电影。

我遇到的问题是 dataTaskWithRequest 的完成处理程序。如果单击其中一个 collectionView 单元格,您将转到该选定电影的 detailView。然而,它并不总是有效。我收到一条错误消息:展开时意外发现 nil。那是因为它没有进入 dataTaskWithRequest 的完成处理程序,而是直接进入 detailVC 并尝试在标题标签、流派标签等中传递数据但没有数据。

我希望你们知道问题出在哪里,因为我已经尝试过,但我看不出问题出在哪里。

或者,这个问题是因为以前的事情而发生的吗?因为首先我使用“by search”而不是“by ID”从http://www.omdbapi.com/ 检索数据。然后从那里检索 ID,然后从该 ID 中检索 detailVC 的数据。

这是我的代码:

 // Go to detail view of selected movie
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {

    let selectedMovieId = self.IDs[indexPath.row]

    chosenMovieId = selectedMovieId

    self.performSegueWithIdentifier("showDetail", sender: self)
}

// Preparations before going to the detail view of selected movie
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

    if segue.identifier == "showDetail" {

        _ = self.movieInfoFrom(searchMovie: chosenMovieId, segue: segue)
    }
}


func movieInfoFrom(searchMovie movieId: String, segue: UIStoryboardSegue) {

    let movieUrlString = "http://www.omdbapi.com/?i=\(movieId)&y=&plot=full&r=json"
    let url = NSURL(string: movieUrlString)
    print(movieUrlString)

    let urlRequest = NSURLRequest(URL: url!)
    let urlSession = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())

    let urlTask = urlSession.dataTaskWithRequest(urlRequest) { (data, response, error) in

        if error == nil {

            // Convert data to JSON
            let swiftyJSON = JSON(data: data!)

            let title = swiftyJSON["Title"].string!
            let runTime = swiftyJSON["Runtime"].string!
            let genre = swiftyJSON["Genre"].string!
            let plot = swiftyJSON["Plot"].string!
            let rating = swiftyJSON["imdbRating"].string!
            let year = swiftyJSON["Year"].string!
            let poster = swiftyJSON["Poster"].string

            self.infoResult = ["\(title)", "\(runTime)", "\(genre)", "\(plot)", "\(rating)", "\(year)"]
            print("\(self.infoResult)")

            let destinationVC = segue.destinationViewController as! MovieDetailController

            destinationVC.movieDetails = self.infoResult
            destinationVC.moviePoster = poster
        }
    }

    urlTask.resume()
}

【问题讨论】:

    标签: ios swift nsurlsession nsurl


    【解决方案1】:

    我尝试修复您的代码并用一些 cmets 进行解释:

    func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
    
        let selectedMovieId = self.IDs[indexPath.row]
    
        chosenMovieId = selectedMovieId
    
        self.movieInfoFrom(searchMovie: chosenMovieId)
    }
    
    // Preparations before going to the detail view of selected movie
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    
        if segue.identifier == "showDetail" {
    
            let destinationVC = segue.destinationViewController as! MovieDetailController
    
                destinationVC.movieDetails = self.infoResult
                destinationVC.moviePoster = poster
        }
    }
    
    
    func movieInfoFrom(searchMovie movieId: String) {
    
        let movieUrlString = "http://www.omdbapi.com/?i=\(movieId)&y=&plot=full&r=json"
        let url = NSURL(string: movieUrlString)
        print(movieUrlString)
    
        let urlRequest = NSURLRequest(URL: url!)
        let urlSession = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
    
        // This is asynchronously, you can put a loading here 
        let urlTask = urlSession.dataTaskWithRequest(urlRequest) { (data, response, error) in
            // Got response, stop loading here
            if error == nil {
    
                // Convert data to JSON
                let swiftyJSON = JSON(data: data!)
    
                let title = swiftyJSON["Title"].string!
                let runTime = swiftyJSON["Runtime"].string!
                let genre = swiftyJSON["Genre"].string!
                let plot = swiftyJSON["Plot"].string!
                let rating = swiftyJSON["imdbRating"].string!
                let year = swiftyJSON["Year"].string!
    
                // You can save the poster as local variable
                let poster = swiftyJSON["Poster"].string
    
                self.infoResult = ["\(title)", "\(runTime)", "\(genre)", "\(plot)", "\(rating)", "\(year)"]
                print("\(self.infoResult)")
    
                // This should be call on main thread
                dispatch_async(dispatch_get_main_queue()) {
                    self.performSegueWithIdentifier("showDetail", sender: self)
                }
    
    
            }
        }
    
        urlTask.resume()
    }
    

    【讨论】:

    • 感谢您的帮助。它通过重新排列一些代码和功能行来工作。你能向我解释为什么这个解决方案可以解决问题吗?是不是因为我调用 perfomSegue 太快了?
    • 那是因为你的 URL 任务是异步的,也就是说它会立即返回,并且在你的任务结束之前已经完成了 segue 的准备工作。
    • 我不明白你的意思。如果我在所有更改之前在我的代码中使用断点,我可以看到它转到函数 prepareForSegue。然后从那里转到函数 movieInfoFrom,如果 URL 任务完成,它会进入完成处理程序,然后在模拟器上显示 detailView。这是否意味着prepareForSegue在完成了movieInfoFrom函数中的代码块后就结束了?
    • 没有。这意味着您的网络足够快,可以快速获得响应... prepareForSegue 肯定会在您的代码块执行之前完成,因为它在另一个线程上异步运行。
    【解决方案2】:

    试试这个代码,带有安全的选项

    func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
    
        chosenMovieId = self.IDs[indexPath.row]
        movieInfoFrom(searchMovie: chosenMovieId)
    
    }
    
    func movieInfoFrom(searchMovie movieId: String) {
    
        let movieUrlString = "http://www.omdbapi.com/?i=\(movieId)&y=&plot=full&r=json"
        let url = NSURL(string: movieUrlString)
        print(movieUrlString)
    
        let urlRequest = NSURLRequest(URL: url!)
        let urlSession = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
    
        let urlTask = urlSession.dataTaskWithRequest(urlRequest) { (data, response, error) in
    
            if error == nil && data != nil {
    
                // Convert data to JSON
                let swiftyJSON = JSON(data: data!)
    
                let title = swiftyJSON["Title"].string ?? ""
                let runTime = swiftyJSON["Runtime"].string ?? ""
                let genre = swiftyJSON["Genre"].string ?? ""
                let plot = swiftyJSON["Plot"].string ?? ""
                let rating = swiftyJSON["imdbRating"].string ?? ""
                let year = swiftyJSON["Year"].string ?? ""
                let poster = swiftyJSON["Poster"].string
    
                self.infoResult = ["\(title)", "\(runTime)", "\(genre)", "\(plot)", "\(rating)", "\(year)"]
                print("\(self.infoResult)")
    
                dispatch_async(dispatch_get_main_queue()) {
                    self.performSegueWithIdentifier("showDetail", sender: poster)
                }
            }
        }
        urlTask.resume()
    }
    
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    
        if segue.identifier == "showDetail", let destinationVC = segue.destinationViewController as? MovieDetailController {
    
            destinationVC.movieDetails = self.infoResult
            destinationVC.moviePoster = sender as? String
        }
    }
    

    【讨论】:

    • 感谢您帮助我。通过重新排列一些行和功能的代码,它起作用了。你能向我解释为什么这个解决方案可以解决问题吗?是不是因为我调用 performSegue 太快了?
    猜你喜欢
    • 2014-05-01
    • 2019-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多