簡體   English   中英

如何使用NSOperationQueue和NSBlockOperation在Swift中使JSON響應異步?

[英]How to make a JSON Response asynchronous in Swift with NSOperationQueue and NSBlockOperation?

我只想使JSON響應與提到的類異步。 我特別想使用我的NSOperation類中的方法。 這對我來說似乎很復雜,因為我不知道方法“ parseResponse”的參數應來自何處。 這是我的嘗試+相關代碼。

我感謝任何幫助。

編輯了代碼和注釋以清除問題。 我知道我的舊代碼已經是異步的,但是我想重寫它,所以新的解決方案是使用NSOperationQueue和NSBlockOperation。

func jsonParser() {
    let urlPath = "https://api.github.com/users/\(githubName)/repos"

    guard let endpoint = NSURL(string: urlPath) else {
        print("Error creating endpoint")
        let alert = UIAlertController(title: "Error Github Name Request", message: "Error creating endpoint", preferredStyle: UIAlertControllerStyle.Alert)
        let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default) {
            UIAlertAction in
            self.navigationController?.popToRootViewControllerAnimated(true)
        }
        alert.addAction(okAction)
        self.presentViewController(alert, animated: true, completion: nil);

        return
    }


   // How would I implement this part of the code, to make the response asynchronous? 

 let queue = NSOperationQueue()
 let o = JSONParser()



var blockOperation = NSBlockOperation { () -> Void in

    NSURLSession.sharedSession().dataTaskWithRequest(request) { (data, response, error) in

        o.parseResponse(data, response: response, error: error, completionHandler: (parsedData: [ViewControllerTableView.Repository], error: NSError) -> () )


        }.resume()
    }



queue.addOperation(blockOperation)




// The commented code below is my old solution. And I want to take my old
 //solution and rewrite it so it actually uses NSOperationQueue and 

// NSBlockOperation 

/*
    let request = NSMutableURLRequest(URL:endpoint)
    NSURLSession.sharedSession().dataTaskWithRequest(request) { (data, response, error) in
        do {
            guard let data = data else {
                let alert = UIAlertController(title: "Error Github Name Request", message: "\(JSONError.NoData)", preferredStyle: UIAlertControllerStyle.Alert)

                let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default) {
                    UIAlertAction in
                    self.navigationController?.popToRootViewControllerAnimated(true)
                }

                alert.addAction(okAction)
                self.presentViewController(alert, animated: true, completion: nil);
                throw JSONError.NoData
            }

            guard let json = try NSJSONSerialization.JSONObjectWithData(data, options:.AllowFragments) as? [[String: AnyObject]] else {

                let alert = UIAlertController(title: "Error Github Name Request", message: "\(JSONError.ConversionFailed)", preferredStyle: UIAlertControllerStyle.Alert)

                let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default) {
                    UIAlertAction in
                    self.navigationController?.popToRootViewControllerAnimated(true)
                }
                alert.addAction(okAction)
                self.presentViewController(alert, animated: true, completion: nil);
                throw JSONError.ConversionFailed
            }

            self.owner=json[0]["owner"]!["login"]! as! String
            self.allRepos.removeAll()
            for a in json {
                let temp:Repository = Repository(id: (a["id"] as? Int)!,name: (a["name"] as? String), size: (a["size"] as? Int)!, watchers: (a["watchers"] as? Int), created_at: (a["created_at"] as? String)!, descr: (a["description"] as? String)!)
                self.allRepos.append(temp)
            }
            self.tableRefresh(self.allRepos)

        } catch let error as JSONError {
            print(error.rawValue)
        } catch let error as NSError {
            print(error.debugDescription)
        }
        }.resume()
*/
}



class JSONParser {


    func parseResponse(data: NSData?, response:
        NSURLResponse?,error:
        NSError?,completionHandler: (parsedData:
        [Repository],error: NSError) -> ())
    {


    }

}

為了將異步NSURLSession任務包裝在一個操作中,您必須使用一個異步的,自定義的NSOperation子類。 不幸的是, NSBlockOperation甚至是標准的NSOperation子類都被設計為處理同步任務(即,一旦同步任務完成,操作就完成了)。

但是在這種情況下,我們正在處理異步任務。 因此,我們必須將NSOperation子類NSOperation並明確告訴它是異步的。 此外,我們必須使用isExecutingisFinished特殊KVO isExecuting使此異步行為正常工作。 有關更多信息,請參見《 並發編程指南》中的“ 配置並發執行操作 ”。

我個人將其包含在抽象的AsynchronousOperation類中:

/// Asynchronous Operation base class
///
/// This class performs all of the necessary KVN of `isFinished` and
/// `isExecuting` for a concurrent `NSOperation` subclass. So, to developer
/// a concurrent NSOperation subclass, you instead subclass this class which:
///
/// - must override `main()` with the tasks that initiate the asynchronous task;
///
/// - must call `completeOperation()` function when the asynchronous task is done;
///
/// - optionally, periodically check `self.cancelled` status, performing any clean-up
///   necessary and then ensuring that `completeOperation()` is called; or
///   override `cancel` method, calling `super.cancel()` and then cleaning-up
///   and ensuring `completeOperation()` is called.

public class AsynchronousOperation : NSOperation {

    override public var asynchronous: Bool { return true }

    private let stateLock = NSLock()

    private var _executing: Bool = false
    override private(set) public var executing: Bool {
        get {
            return stateLock.withCriticalScope { _executing }
        }
        set {
            willChangeValueForKey("isExecuting")
            stateLock.withCriticalScope { _executing = newValue }
            didChangeValueForKey("isExecuting")
        }
    }

    private var _finished: Bool = false
    override private(set) public var finished: Bool {
        get {
            return stateLock.withCriticalScope { _finished }
        }
        set {
            willChangeValueForKey("isFinished")
            stateLock.withCriticalScope { _finished = newValue }
            didChangeValueForKey("isFinished")
        }
    }

    /// Complete the operation
    ///
    /// This will result in the appropriate KVN of isFinished and isExecuting

    public func completeOperation() {
        if executing {
            executing = false
        }

        if !finished {
            finished = true
        }
    }

    override public func start() {
        if cancelled {
            finished = true
            return
        }

        executing = true

        main()
    }

    override public func main() {
        fatalError("subclasses must override `main`")
    }
}

extension NSLock {

    /// Perform closure within lock.
    ///
    /// An extension to `NSLock` to simplify executing critical code.
    ///
    /// - parameter block: The closure to be performed.

    func withCriticalScope<T>(@noescape block: Void -> T) -> T {
        lock()
        let value = block()
        unlock()
        return value
    }
}

一旦有了這些,進行執行網絡請求的操作就非常簡單:

/// Simple network data operation
///
/// This can be subclassed for image-specific operations, JSON-specific operations, etc.

class DataOperation : AsynchronousOperation {
    var request: NSURLRequest
    var session: NSURLSession
    weak var downloadTask: NSURLSessionTask?
    var networkCompletionHandler: ((NSData?, NSURLResponse?, NSError?) -> ())?

    init(session: NSURLSession, request: NSURLRequest, networkCompletionHandler: (NSData?, NSURLResponse?, NSError?) -> ()) {
        self.session = session
        self.request = request
        self.networkCompletionHandler = networkCompletionHandler

        super.init()
    }

    override func main() {
        let task = session.dataTaskWithRequest(request) { data, response, error in
            self.networkCompletionHandler?(data, response, error)
            self.completeOperation()
        }
        task.resume()
        downloadTask = task
    }

    override func cancel() {
        super.cancel()

        downloadTask?.cancel()
    }

    override func completeOperation() {
        networkCompletionHandler = nil

        super.completeOperation()
    }
}

而且,如果您想要解析JSON的操作,則可以將其子類化:

class GitRepoOperation: DataOperation {
    typealias ParseCompletionHandler = (String?, [Repository]?, ErrorType?) -> ()

    let parseCompletionHandler: ParseCompletionHandler

    init(session: NSURLSession, githubName: String, parseCompletionHandler: ParseCompletionHandler) {
        self.parseCompletionHandler = parseCompletionHandler
        let urlPath = "https://api.github.com/users/\(githubName)/repos"
        let url = NSURL(string: urlPath)!
        let request = NSURLRequest(URL: url)
        super.init(session: session, request: request) { data, response, error in
            guard let data = data where error == nil else {
                parseCompletionHandler(nil, nil, JSONError.NoData)
                return
            }

            do {
                guard let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [[String: AnyObject]] else {
                    parseCompletionHandler(nil, nil, JSONError.ConversionFailed)
                    return
                }

                guard let owner = json.first?["owner"]?["login"] as? String else {
                    parseCompletionHandler(nil, nil, JSONError.MissingFields)
                    return
                }

                var repos = [Repository]()
                for a in json {
                    guard let id = a["id"] as? Int, let size = a["size"] as? Int, let createdAt = a["created_at"] as? String, let descr = a["description"] as? String else {
                        parseCompletionHandler(nil, nil, JSONError.MissingFields)
                        return
                    }

                    repos.append(Repository(id: id, name: a["name"] as? String, size: size, watchers: a["watchers"] as? Int, created_at: createdAt, descr: descr))
                }
                parseCompletionHandler(owner, repos, nil)
            } catch {
                parseCompletionHandler(nil, nil, JSONError.ConversionFailed)
            }
        }
    }
}

請注意,我已經刪除了那些強制解包運算符( ! ),而是進行了可選的綁定以優雅地檢測丟失的字段(至少對於非可選字段而言)。 我還添加了其他錯誤類型,因此調用方可以區分不同類型的錯誤。 但是,最重要的是,您可能要使用! 運營商更加明智。

調用它,您只需執行以下操作:

let operation = GitRepoOperation(session: session, githubName: name) { owner, repos, error in
    guard let owner = owner, let repos = repos where error == nil else {
        // handle the error however you want here
        return
    }

    // handle `owner` and `repos` however you want here
}
queue.addOperation(operation)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM