NSURL Swift 中的连接:如果 URL 是 404,如何在完成加载后调用函数并重新启动它?

NSURLConnection in Swift: How to call a function when it's finished loading, and restart it if the URL is a 404?

我正在使用 Google 图片 API 生成这样的图片:

let url = NSURL(string: "https://ajax.googleapis.com/ajax/services/search/images?v=1.0&q=seattle")
let request = NSURLRequest(URL: url!)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()){ (response, go, error) -> Void in            
let go = NSJSONSerialization.JSONObjectWithData(go, options: NSJSONReadingOptions.AllowFragments, error: nil) as [String:AnyObject]
let responseData = go["responseData"] as [String:AnyObject]

然后剩下的代码向下挖掘以找到图像的 URL google api 给出并设置(此处为 var theurl)应用程序中的图像:

let data = NSData(contentsOfURL: theurl!)
self.mainImage.image = UIImage(data: data!)

这行得通,但我需要 1) 停止进程并在发现无法加载的图像 URL 时调用一个函数,以及 2) 在图像完成加载后调用一个函数。

使用完成块并检查你得到的NSError

NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
    if let theError = error{
       if theError.code == 404{
        //Image not available
       }else{
        //something else
       }
    }
})

您这里有一些问题需要解决。我认为通过一个例子会更有意义。以下是你完全相同的逻辑,只是稍微更稳健。

import UIKit

class ImageViewController: UIViewController {
    var mainImage: UIImageView!
    let operationQueue: NSOperationQueue = {
        let queue = NSOperationQueue()
        queue.maxConcurrentOperationCount = 4
        queue.qualityOfService = NSQualityOfService.Utility

        return queue
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        downloadImage { [weak self] image in
            if let strongSelf = self {
                if let image = image {
                    strongSelf.mainImage.image = image
                }
            }
        }
    }

    func downloadImage(completion: UIImage? -> Void) {
        let url = NSURL(string: "https://ajax.googleapis.com/ajax/services/search/images?v=1.0&q=seattle")
        let request = NSURLRequest(URL: url!)

        NSURLConnection.sendAsynchronousRequest(request, queue: self.operationQueue) { [weak self] response, data, error in
            if let strongSelf = self {
                if error != nil || data == nil {
                    println(error) // failed to complete original google api request
                    completion(nil)
                    return
                }

                var serializationError: NSError?
                if let go = NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments, error: &serializationError) as? [String: AnyObject] {
                    let responseData = go["responseData"] as [String:AnyObject]
                    let imageURL = NSURL(string: "some_image_url_from_response_data")! // placeholder
                    let imageRequest = NSURLRequest(URL: url!)

                    NSURLConnection.sendAsynchronousRequest(imageRequest, queue: strongSelf.operationQueue) { response, data, error in
                        if error != nil || data == nil {
                            println(error) // failed to download the image
                            completion(nil)
                            return
                        }

                        if let image = UIImage(data: data!) {
                            completion(image)
                        } else {
                            println("Failed to create UIImage from downloaded image data")
                            completion(nil)
                        }
                    }
                } else {
                    println(serializationError)
                    completion(nil)
                }
            }
        }
    }
}

异步操作队列

首先,您在主队列上发出异步请求,这违背了异步行为的全部目的。相反,我创建了一个并发操作队列 属性 用于处理下载操作。

弱化/强化

任何时候你在做异步的事情,你必须非常小心保留 self。保证您始终正确执行的最安全方法是弱化/加强以避免创建保留循环。

嵌套异步请求

您应该使用另一个 sendAsynchronousRequest 调用在后台下载图像。否则,您将在下载图像时阻塞主线程。您的用户不会对这种行为感到满意!

安全反序列化

检查您的 JSON 解析是否成功非常重要。服务器有时会做一些奇怪的事情,您的代码应该能够毫无问题地处理这些事情。

Alamofire

你看过Alamofire了吗?它使类似的事情变得容易得多。

希望这有助于阐明一些问题。