Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Swift中使用OperationQueue下载和解析JSON_Json_Swift_Nsoperationqueue - Fatal编程技术网

如何在Swift中使用OperationQueue下载和解析JSON

如何在Swift中使用OperationQueue下载和解析JSON,json,swift,nsoperationqueue,Json,Swift,Nsoperationqueue,我陷入了一个概念上简单的问题。发生的情况是,解析操作在下载操作的完成处理程序完成之前执行。因此,没有需要解析的数据。您可以将以下代码直接放入文件并运行它 如何确保在运行解析操作之前完成下载 import UIKit let search = "https://api.nal.usda.gov/ndb/search/?format=json&q=butter&sort=n&max=25&offset=0&api_key=DEMO_KEY" class V

我陷入了一个概念上简单的问题。发生的情况是,解析操作在下载操作的完成处理程序完成之前执行。因此,没有需要解析的数据。您可以将以下代码直接放入文件并运行它

如何确保在运行解析操作之前完成下载

import UIKit

let search = "https://api.nal.usda.gov/ndb/search/?format=json&q=butter&sort=n&max=25&offset=0&api_key=DEMO_KEY"

class ViewController: UIViewController {



    override func viewDidLoad() {
        super.viewDidLoad()

        let fetch = FetchNBDNumbersOperation()
        let parse = NDBParseOperation()

        // 1
        let adapter = BlockOperation() { [unowned parse, unowned fetch] in
            parse.data = fetch.data
        }

        // 2
        adapter.addDependency(fetch)
        parse.addDependency(adapter)

        // 3
        let queue = OperationQueue()
        queue.addOperations([fetch, parse, adapter], waitUntilFinished: true)
    }
}

class FetchNBDNumbersOperation: Operation {

    var data: Data?

    override func main() {
        let url = URL(string: search)!
        let urlSession = URLSession.shared
        let dataTask = urlSession.dataTask(with: url) { (jsonData, response, error) in
            guard let jsonData = jsonData, let response = response else {
                debugPrint(error!.localizedDescription)
                return
            }
            self.data = jsonData
            print("Response URL: \(String(describing: response.url?.absoluteString))")
        }
        dataTask.resume()
    }
}

class NDBParseOperation: Operation {

    var data: Data?
    var nbdNumbers = [NBDNumber]()

    override func main() {
        let decoder = JSONDecoder()
        do {
            guard let jsonData = self.data else {
                fatalError("No Data")
            }
            let dictionary = try decoder.decode( [String: USDAFoodSearch].self, from: jsonData )
            for (_, foodlist) in dictionary {
                for food in foodlist.item {
                    print("\(food.name) \(food.ndbno) \(food.group)")
                    let nbdNumber = NBDNumber(name: food.name, nbdNo: food.ndbno)
                    nbdNumbers.append(nbdNumber)
                }
            }
        } catch {
            print(error.localizedDescription)
        }
    }
}

struct NBDNumber {
    var name: String
    var nbdNo: String
}

struct USDAFoodSearch: Decodable {
    let q: String
    let sr: String
    let ds: String
    let start: Int
    let end: Int
    let total: Int
    let group: String
    let sort: String
    let item: [USDAFood]

    struct USDAFood: Decodable {
        let offset: Int     //Position in Array
        let group: String
        let name: String
        let ndbno: String
        let ds: String
    }
}

您正在将其过度复杂化,甚至不需要使用OperationQueue,因为数据任务将异步完成

你可以这样做:

class FetchAndParse {
    var data: Data?
    var nbdNumbers = [NBDNumber]()

    func fetch() {
        let url = URL(string: search)!
        let urlSession = URLSession.shared
        let dataTask = urlSession.dataTask(with: url) { (jsonData, response, error) in
            guard let jsonData = jsonData, let response = response else {
                debugPrint(error!.localizedDescription)
                return
            }
            self.data = jsonData
            print("Response URL: \(String(describing: response.url?.absoluteString))")

            self.parse()
        }
        dataTask.resume()
    }

    func parse() {
        let decoder = JSONDecoder()
        do {
            guard let jsonData = self.data else {
                fatalError("No Data")
            }
            let dictionary = try decoder.decode( [String: USDAFoodSearch].self, from: jsonData )
            for (_, foodlist) in dictionary {
                for food in foodlist.item {
                    print("\(food.name) \(food.ndbno) \(food.group)")
                    let nbdNumber = NBDNumber(name: food.name, nbdNo: food.ndbno)
                    nbdNumbers.append(nbdNumber)
                }
            }
            print ("Finished With \(nbdNumbers.count) Items")
        } catch {
            print(error.localizedDescription)
        }
    }
}
let fp = FetchAndParse()
print ("Before Fetch")
fp.fetch()
print ("After Fetch")
然后您可以这样使用:

class FetchAndParse {
    var data: Data?
    var nbdNumbers = [NBDNumber]()

    func fetch() {
        let url = URL(string: search)!
        let urlSession = URLSession.shared
        let dataTask = urlSession.dataTask(with: url) { (jsonData, response, error) in
            guard let jsonData = jsonData, let response = response else {
                debugPrint(error!.localizedDescription)
                return
            }
            self.data = jsonData
            print("Response URL: \(String(describing: response.url?.absoluteString))")

            self.parse()
        }
        dataTask.resume()
    }

    func parse() {
        let decoder = JSONDecoder()
        do {
            guard let jsonData = self.data else {
                fatalError("No Data")
            }
            let dictionary = try decoder.decode( [String: USDAFoodSearch].self, from: jsonData )
            for (_, foodlist) in dictionary {
                for food in foodlist.item {
                    print("\(food.name) \(food.ndbno) \(food.group)")
                    let nbdNumber = NBDNumber(name: food.name, nbdNo: food.ndbno)
                    nbdNumbers.append(nbdNumber)
                }
            }
            print ("Finished With \(nbdNumbers.count) Items")
        } catch {
            print(error.localizedDescription)
        }
    }
}
let fp = FetchAndParse()
print ("Before Fetch")
fp.fetch()
print ("After Fetch")
如果运行该命令,您将看到“Before Fetch”和“After Fetch”消息都在下载和解析完成之前显示,并且解析在正确获取之后发生


当然,您可能需要对其进行更新,以便类通知某些内容所有内容都已完成,因此可能需要一个完成处理程序或委托,但这将由您决定。

在获取操作中,您可以继续执行
URLSessionDataTask
。此时,操作认为它已经完成了,因为它不知道其他线程上发生了什么,也不关心并发操作,并且它依赖(
adapter
)启动。同时,
URLSessionDataTask
仍在另一个线程上执行

从苹果的文档中,在
NSOperation

对于非并发操作,通常只重写一个方法:

  • main()
URLSessionDataTask
s同时运行,因此您需要做更多的工作来将它们包装到
NSOperation
中。为了包装并发操作,您需要:

…至少重写以下方法和属性:

start()

isAsynchronous

i执行

isFinished


NSOperation
在这方面有很多细节,但总而言之:您需要重写
start()
而不是
main()
,并让您的实现保持操作状态最新。

您只是在使用
main
操作队列方法。
并且您没有向操作队列通知操作状态

由于jjatie
操作队列
需要isExecutingisFinishedKVC通知队列操作已完成或正在执行

我的建议是,在进行如此复杂的操作之前,请先阅读文档

下面是一个示例代码

class WSOperations: Operation {

    private var _executing = false
    private var _finished = false
    private var showHUD:HUDFlag = .show

    override var isExecuting: Bool {
        get {
            return _executing
        } set {
            willChangeValue(forKey: "isExecuting")
            _executing = newValue
            didChangeValue(forKey: "isExecuting")

        }

    }

    override var isFinished: Bool {
        get {
            return _finished
        } set {
            willChangeValue(forKey: "isFinished")
            _finished = newValue
            didChangeValue(forKey: "isFinished")
        }
    }



    override func start() {
        if isCancelled {
            isFinished = true

            return
        }

        isExecuting = true

        func completeOperation() {
            isFinished = true
            isExecuting = false
            Logger.log(message: "Operation finished")
        }

        //Your request
           request =  DataManager.sharedManager.getRequest(showHUD: showHUD, success: { (success, response) in
                if let t = self.finishedBLock {
                    t.success(success, response)
                }
                completeOperation()
            }, failure: { (error) in
                if let t = self.finishedBLock {
                    t.failure(error)
                }
                completeOperation()

            })


    }
    override func cancel() {
        super.cancel()
        if isExecuting {
            isFinished = true
            isExecuting = false
        }

        request?.cancel()
    }
}

希望对您有所帮助

以下是答案。使用下面的类的子类获取操作。并在Fetch Op completion处理程序的末尾告诉它操作已完成

class FetchNBDNumbersOperation: AsynchronousOperation {

    var data: Data?

    override func main() {
        super.main()

        let url = URL(string: search)!
        let urlSession = URLSession.shared
        let dataTask = urlSession.dataTask(with: url) { (jsonData, response, error) in
            guard let jsonData = jsonData, let response = response else {
                debugPrint(error!.localizedDescription)
                return
            }
            self.data = jsonData
            print("Response URL: \(String(describing: response.url?.absoluteString))")
            self.state = .finished
        }
        dataTask.resume()
    }
}
在此处找到异步子类:


以我的拙见,我认为您应该重新设计它,以便只包含一个包含“获取”和“解析”的操作。因此,当您完成获取数据,然后解析数据时,不需要为两者都单独操作,因为它们无论如何都是相互关联的。感谢您的建议,但是操作是必需的,因为还有其他操作依赖于前两个,如果没有它们,它将变成无法维护的混乱。您应该提到,最初,否则我们正在解决一个问题将信息减半的问题。有用的线索。谢谢。这是一个很好的解决方案,但是永远不要调用super.start(),也不需要super.main()。实际上,您并没有在代码中这样做,而是在注释中这样做。