2016-11-04 7 views
1

AppleのGCDを初めてお使いで、DispatchGroupに問題があります。 投稿が完全に初期化された後、地図に投稿を添付しようとしています。 post.initの内部にはURLSessionがあり、URLからUIImageをダウンロードします。しかし、ダウンロードが完了する前でさえ、group.notifyは発砲されます。私は本当に理由が分からない。以下はコードです。私は本当に助言や助けに感謝します!ありがとう。作業が完了する前にディスパッチグループに通知します

// ViewController.swift 
let group = DispatchGroup 
... 
... 

group.enter() 
DispatchQueue.global(qos: .userInitiated).async { 
    post = Post(values: post) 
    self.posts[postId] = post 
    group.leave() 
} 

group.notify(queue: DispatchQueue.main, execute: { 
    print("notify:: \(post?.picture)") // This prints out nil, when it shouldn't. 
    self.addPostToMap(post!, at: location!) 
}) 

// Post.swift 
class Post { 
    var picture: UIImage? 
    var thumbnail: UIImage? 
    init(values: [String: Any]) { 
     ... 
     URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) in 
      DispatchQueue.main.async { 
       self.picture = UIImage(data: data!) 
       self.thumbnail = Util.resizeImage(image: self.picture!, targetSize: CGSize(width: 50, height: 50)) 
      } 

     }).resume() 

    } 

} 

答えて

2

init(values :)内の補完ハンドラは、タスクが完了したときにいつでも起動されます。しかし、.resume()を呼び出した後、コードの実行が続行されます。つまり、initが終了し、group.notifyブロックがすぐに呼び出されます。

これはさまざまな方法で処理できます。 1つは、Postクラスのデリゲートプロトコルを設定し、そのデリゲートとしてViewControllerを設定することです。あなたの補完ハンドラでは、引数を呼ぶために関数を呼び出すことができました。それは、タスクが終了したことをView Controllerに伝えるdidFinish()です。その関数で通知をラップすることができます。

このようにして、私はinit関数からdataTaskを取り出します。これは、デリゲートが暗黙のうちにアンラップされたオプションとして宣言され、作成クラスから設定されるためです。したがって、クラスを初期化するときに、代理コールバックを呼び出すときに代理人が何も参照しない可能性があります。次のように

だから構造は次のようになります。

// ViewController.swift 
class ViewController : UIViewController, PostDelegate { 
    let group = DispatchGroup 
    ... 
    ... 

    group.enter() 
    DispatchQueue.global(qos: .userInitiated).async { 
     post = Post(values: post) 
     post.delegate = self 
     post.getImage() // New function 
     self.posts[postId] = post 
     group.leave() 
    } 

    .... 

    func didFinish() -> Void { 
     group.notify(queue: DispatchQueue.main, execute: { 
      print("notify:: \(post?.picture)") 
      self.addPostToMap(post!, at: location!) 
    }) 

    .... 

Protocol PostDelegate { 
    func didFinish() -> Void 
} 

// Post.swift 
class Post { 
    var delegate : PostDelegate! 
    var picture: UIImage? 
    var thumbnail: UIImage? 
    init(values: [String: Any]) { 
    ... 
    } 

    func getImage() -> Void { 
     URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) in 
      DispatchQueue.main.async { 
       self.picture = UIImage(data: data!) 
       self.thumbnail = Util.resizeImage(image: self.picture!, targetSize: CGSize(width: 50, height: 50)) 
       if delegate != nil { 
        delegate.didFinish() // Tell the delegate you are done 
       } 
      } 

     }).resume() 

    } 

} 

理にかなっている希望、そしてより多くの、重要なのは、それが役立つこと!

+0

返事ありがとうございますが、 'didFinish'の変数' post'と 'location'にはアクセスできないので、通知を出すのは問題になります。これに対する提案はありますか? –

+0

実際に私はそれを作った!私は代理メソッドを介して変数を渡しました。ありがとう! –

関連する問題