我想只让JSON Response与提到的类异步。 我特别想使用我在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) -> ())
{
}
}
答案 0 :(得分:1)
为了在操作中包装异步NSURLSession
任务,您必须使用异步的自定义NSOperation
子类。不幸的是,NSBlockOperation
,甚至是标准的NSOperation
子类,都是为处理同步任务而设计的(即一旦同步任务完成,操作就会完成)。
但在这种情况下,我们正在处理异步任务。因此,我们必须子类NSOperation
并明确告诉它它是异步的。此外,我们必须使用isExecuting
和isFinished
的特殊KVO,以使此异步行为正常工作。有关更多信息,请参阅并发编程指南中的Configuring Operations for Concurrent Execution。
就个人而言,我在一个抽象的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)