如何使用dispatchQueue.main从Swift中的闭包中传递值

时间:2017-05-22 12:24:29

标签: json swift xcode closures

我需要澄清一些关于将值传递出来的说明。我认为问题是使用dispatchQueue.main但我无法理解在哪里以及为什么

这是代码:

import UIKit

class ViewController: UIViewController {
var allCard = [Card]()    
let card = Card(name: "", cost: 0, attack: 0, durability: 0, cardClass: "", cardSet: "", imageURL: "", goldenImageURL: "", type: "", mechanics: [["":""]], howToGetGolden: "")

override func viewDidLoad() {
    super.viewDidLoad()
    getCards()
    print(allCard.count)
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

func getCards()  {
    let url = URL(string: "https://omgvamp-hearthstone-v1.p.mashape.com/cards/sets/Classic?mashape-key=....")!        
    let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
        if error != nil { // if urlSession have issues manage the error here
            print(error)
        } else { // if we have data, response, error then continue here
            if let urlContent = data { // if data exist then
                do {
                    let jsonResult = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [Dictionary<String, AnyObject>]
                    for cards in 0...jsonResult.count - 1 {
                        if let name = jsonResult[cards]["name"] as? String {    //NAME Optionaml Bindings
                            self.card.name = name
                        } else {
                            self.card.name = ""
                        }                                                       // END Name Optionaml Bindings
                        if let cost = jsonResult[cards]["cost"] as? Int {       //COST Optionaml Bindings
                            self.card.cost = cost
                        } else {
                            self.card.cost = 0
                        }                                                        // END COST Optionaml Bindings
                        if let attack = jsonResult[cards]["attack"] as? Int {    //ATTACK Optionaml Bindings
                            self.card.attack = attack
                        } else {
                            self.card.attack = 0
                        }                                                       // END TTACK Optionaml Bindings
                } catch { //catch error while parsing json
                    print("error")
                }
            }
        } // end if/ELSE
    } // end of task (closure)
    task.resume()
}
}

我应该如何在这里使用dispatchQueue.main?它是异步还是同步?我试图在互联网上查看,但我找不到我理解的答案/:

感谢您的帮助

4 个答案:

答案 0 :(得分:2)

您的下载需要时间,因此您必须等待它完成才能打印结果。为此,请向getCards()添加完成处理程序:

func getCards(completion: () -> Void)  {
    // ... your code
    let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
        // ... your code
        for resultDict in jsonResult {
            // create a new card ...
            let card = Card(name: resultDict["name"], cost: resultDict["cost"], attack: resultDict["attack"], durability: 0, cardClass: "", cardSet: "", imageURL: "", goldenImageURL: "", type: "", mechanics: [["":""]], howToGetGolden: "")
             // ... and actually add it to your array
             allCard.append(card)   
        }
        completion() // call completion after parsing 
    } // end of task (closure)
    task.resume()
}

并将其称为:

override func viewDidLoad() {
    super.viewDidLoad()
    getCards() {
        // the code in this closure is called after the download finished
        print(allCard.count)
    }
}

更新

URLSession dataTask在后​​台队列上调用它的完成处理程序。因此,如果您必须执行UI内容(必须在主队列上处理),您必须在主队列上调度完成处理程序:

替换:

completion() // call completion after parsing 

使用:

DispatchQueue.main.async() {
    completion() // call completion after parsing 
}

答案 1 :(得分:2)

为您更正了代码。更好的方法是将你的牌移动到func并使其返回牌或如果失败通过关闭则为零。而且你也不需要在&#34;其他&#34; s中设置空字符串和0,因为你的卡已经用这些&#34;默认&#34;值。此外,您现在还有两个阵列 - 一个是func中的本地阵列,最近才下载了卡片。第二类实例变量将保存所有下载过的卡片。

转义意味着你的闭包可以在func返回后执行(在这种情况下,当请求完成时)。

[弱自我] - 我添加了它,因为你的视图控制器可能在请求完成时不存在,所以关闭不会将它保留在堆中并让它继续(例如当你厌倦等待响应时服务器,只需单击或取消并取消此视图控制器。)

我也在主线程上执行完成,因此您可以在闭包中更新UI。

class ViewController: UIViewController {

var allCards = [Card]()

override func viewDidLoad() {
    super.viewDidLoad()
    getCards() { [weak self] cards in
        if cards != nil, self != nil {
            self!.allCards.append(contentsOf: cards!)
            print("Downloaded cards on this session \(cards!.count)")
            print("All downloaded cards \(self!.allCards.count)")
            // you can update UI here
        }
    }
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

func getCards(completion: @escaping ([Card]?)->Void)  {
    let url = URL(string: "https://omgvamp-hearthstone-v1.p.mashape.com/cards/sets/Classic?mashape-key=....")!

    let task = URLSession.shared.dataTask(with: url) {
        (data, response, error) in

        var cards: [Card]? = nil

        if error != nil { // if urlSession have issues manage the error here
            print(error ?? "")
        } else { // if we have data, response, error then continue here
            if let urlContent = data {

                cards = [Card]()

                do {
                    let jsonResult = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [Dictionary<String, AnyObject>]
                    for cards in 0...jsonResult.count - 1 {

                        let card = Card(name: "", cost: 0, attack: 0, durability: 0, cardClass: "", cardSet: "", imageURL: "", goldenImageURL: "", type: "", mechanics: [["":""]], howToGetGolden: "")

                        if let name = jsonResult[cards]["name"] as? String {    //NAME Optionaml Bindings
                            card.name = name
                        }                                                       // END Name Optionaml Bindings
                        if let cost = jsonResult[cards]["cost"] as? Int {       //COST Optionaml Bindings
                            card.cost = cost
                        }                                                       // END COST Optionaml Bindings
                        if let attack = jsonResult[cards]["attack"] as? Int {    //ATTACK Optionaml Bindings
                            card.attack = attack
                        }

                        cards.append(card)

                    }
                    DispatchQueue.main.async { completion(cards) }
                } catch { //catch error while parsing json
                    print("error")
                }
            }
        }
        if cards == nil {
           DispatchQueue.main.async { completion(nil) }
        }
    } // end of task (closure)
    task.resume()
}

}

答案 2 :(得分:0)

您应该在collectwhilecond(f,cond,itr) = begin t = Base.promote_op(f,eltype(itr)) # unofficial and subject to change r = Vector{t}() all( x -> ( y=f(x) ; cond(y) ? (push!(r,y) ; true) : false), itr ) return r end 内创建卡片实例,并在为其分配值后将卡片附加到for loop数组中,方法allCard应更新如下。

gorCards()

答案 3 :(得分:0)

您可以像这样处理URLSession dataTask的完成:

  • typealias Completion = (Void) -> Void添加到您的班级
  • 为getCards方法添加完成处理程序 func getCards(completion: Completion? = nil) {}

  • 现在你可以这样调用这个方法:

    self.getCards() { print(self.allCard.count) }

  • 在dataTask完成后调用completion()

    导入UIKit     class ViewController:UIViewController {

    typealias Completion = (Void) -> Void
    var allCard = [Card]()
    let card = Card(name: "", cost: 0, attack: 0, durability: 0, cardClass: "", cardSet: "", imageURL: "", goldenImageURL: "", type: "", mechanics: [["":""]], howToGetGolden: "")
    
    override func viewDidLoad() {
        super.viewDidLoad()
        getCards() {
            print(allCard.count)
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    func getCards(completion: Completion? = nil)  {
        let url = URL(string: "https://omgvamp-hearthstone-v1.p.mashape.com/cards/sets/Classic?mashape-key=....")!
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            if error != nil { // if urlSession have issues manage the error here
                print(error)
            } else { // if we have data, response, error then continue here
                if let urlContent = data { // if data exist then
                    do {
                        let jsonResult = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [Dictionary<String, AnyObject>]
                        for cards in 0...jsonResult.count - 1 {
                            if let name = jsonResult[cards]["name"] as? String {    //NAME Optionaml Bindings
                                self.card.name = name
                            } else {
                                self.card.name = ""
                            }                                                       // END Name Optionaml Bindings
                            if let cost = jsonResult[cards]["cost"] as? Int {       //COST Optionaml Bindings
                                self.card.cost = cost
                            } else {
                                self.card.cost = 0
                            }                                                        // END COST Optionaml Bindings
                            if let attack = jsonResult[cards]["attack"] as? Int {    //ATTACK Optionaml Bindings
                                self.card.attack = attack
                            } else {
                                self.card.attack = 0
                            }                                                       // END TTACK Optionaml Bindings
                        } catch { //catch error while parsing json
                            print("error")
                        }
                    }
                } // end if/ELSE
            completion?()
            } // end of task (closure)
            task.resume()
        }
    

    }