質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

クロージャ

クロージャは、プログラミング言語における関数オブジェクトの一種です。 引数以外の変数を実行時の環境ではなく、 自身が定義された環境において解決することを特徴とします。

Q&A

解決済

1回答

953閲覧

DispatchQueueとクロージャ

kyokio

総合スコア560

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

クロージャ

クロージャは、プログラミング言語における関数オブジェクトの一種です。 引数以外の変数を実行時の環境ではなく、 自身が定義された環境において解決することを特徴とします。

0グッド

1クリップ

投稿2020/02/19 08:08

#AlamofireとKannaを使ってスクレイピングしてみました
gameのキャラクターの情報をサイトから取ってくるアプリを作ろうと思っています。
最初の画面(viewController)でURLを5個まで貼ってもらい、userDefaultsで保存して画面遷移しデータを取ってきてviewで表形式でそれぞれの値を比較しようと思いました。
イメージ説明

ViewController

1 var urlArray:[String] = [] 2 @IBOutlet weak var textField1: UITextField! 3 @IBOutlet weak var textField2: UITextField! 4 @IBOutlet weak var textField3: UITextField! 5 @IBOutlet weak var textFiled4: UITextField! 6 @IBOutlet weak var textField5: UITextField! 7 8 9 10 @IBAction func buttonPushed(_ sender: UIButton) { 11 if self.textField1.text != ""{ 12 self.urlArray.append(self.textField1.text!) 13 } 14 if self.textField2.text != ""{ 15 self.urlArray.append(self.textField2.text!) 16 } 17 if self.textField3.text != ""{ 18 self.urlArray.append(self.textField3.text!) 19 } 20 if self.textFiled4.text != ""{ 21 self.urlArray.append(self.textFiled4.text!) 22 } 23 if self.textField5.text != ""{ 24 self.urlArray.append(self.textField5.text!) 25 } 26 let userDefaults = UserDefaults.standard 27 userDefaults.set(self.urlArray, forKey: "urlArray") 28 userDefaults.synchronize() 29 performSegue(withIdentifier: "goPlayers", sender: nil) 30 } 31 32 33} 34

PlayersViewController

1var players:[Player] = [] 2 3 override func viewDidLoad() { 4 super.viewDidLoad() 5 // Do any additional setup after loading the view. 6 7 let userDefaults = UserDefaults.standard 8 let urlArray:[String] = userDefaults.array(forKey: "urlArray") as! [String] 9 10 for i in 0..<urlArray.count{ 11 DispatchQueue.global().sync { 12 print(urlArray[i]) 13 self.scrapWebsite(urlArray[i]) 14 print("players.count3:(self.players.count)") 15 } 16 print("players.count1:(self.players.count)") 17 } 18 print(self.players.count) 19 //viewを追加する関数 20 self.SetView(players.count) 21 } 22 23/////////////////////////////////////////////////// 24 struct Player { 25 var playerName:String? 26 var MaxLevel:String? 27 var abilityList:[(name:String,score:String)] = [/*省略しますがタプル配列で26個分、予め項目名(name)と数値(score)を入れて置いてます*/] 28 } 29////////////////////////////////////////////////////// 30 31func scrapWebsite(_ url:String){ 32 //GETリクエスト 指定URLのコードを取得 33 Alamofire.request(url).responseString { response in 34 //Boolで確認 35 print("true or fauls:(response.result.isSuccess)") 36 37 if let html = response.result.value { 38 //plaers配列に追加 39 self.players.append(self.parseHTML(html: html)) 40 print("players.count2:(self.players.count)") 41 } 42 } 43 } 44 45 func parseHTML(html: String)-> (Player){ 46 //戻り値を宣言 47 var returnPlayer = Player() 48 49 if let doc = try? HTML(html: html, encoding: .utf8) { 50 //タイトルをプリント 51 print("doc.titile:(doc.title!)") 52 53 for link in doc.xpath("/html/body/div[2]/div[4]/script[1]/text()"){ 54 //省略しますがreturnPlayerに値を入れる処理をします 55 } 56 } 57 print("returnPlayer:(returnPlayer)") 58 return returnPlayer 59 } 60

##躓いているところはAlamofireとKannaとは全く関係ないところです
ViewControllerでtextField1のみにURLを入れれ実行します。
するとscrapWeb()でplayers配列にperseHTML()の戻り値(returnPlayer)を追加しているのにprint("players.count2:(self.players.count)")ではplayers.countが1となりますが、print("players.count1:(self.players.count)"),print("players.count3:(self.players.count)")では0となってしまします。
###クロージャの処理はその関数の処理が終わってから始まるのか?

PlayersViewController

1 for i in 0..<urlArray.count{ 2 DispatchQueue.global().sync { 3 print(urlArray[i]) //(1) 4 self.scrapWebsite(urlArray[i]) //(2) 5 print("players.count3:(self.players.count)" //(3) 6 } 7 print("players.count1:(self.players.count)" //(4) 8 }

これだとglobal()が並列処理でsyncが同期処理なので、処理が開始される順番は(1)→(2)→(3)→(4)となり、並列処理なので(1),(2),(3)の終了の順番はまちまちで、(1),(2),(3)の終了後に(4)が開始され終了されるのではないでしょうか?
"start"/"end"ログを見ると、おそらくクロージャの処理はscrapWebsite関数の処理が終了した後に実行されていると思うのですがそういう考えでいいのでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

global()は並列処理そのものではなく、グローバルなキューを取得するという意味です。そして、syncは、キューに同期処理を追加するという意味になります。

for文の動作ですが、
1: i = 0の時のクロージャーをグローバルキューに積む
2: (4)を実行する
3: i = 1の時のクロージャーをグローバルキューに積む
4: (4)を実行する
5: i = urlArray.countになるまで繰り返し
となります。このfor文は、メインキューで実行されます。

グローバルキューはメインキュートは独立したキューなので、for文が動いている途中でもクロージャーの中身を実行します。

外から見ると、メインキューで(4)を指定回数実行、グローバルキューで(1〜3)を指定回数実行となりますが、タイミングは(4)と(1〜3)の計算時間に依存し、どちらかが終わるまでどちらかが待っているということはありません。

投稿2020/02/19 08:32

編集2020/02/19 08:33
AOKINAO

総合スコア268

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kyokio

2020/02/19 09:02

回答ありがとうございます。 キュー 、スレッドがまだ完全に理解できてないのでもう少し勉強します。 今回はスクレイピングでキャラクターの情報(最大5つ)を取ってきて配列に格納してから、viewを表示する関数をを実行したいと考えています。 直列、同期処理をすれば前の処理の完了を待つので解決すると思いやってみましたが、クロージャはクロージャの処理が完了する前に次の処理に移ってしますのでしょうか?
AOKINAO

2020/02/19 23:30 編集

すいません、いろいろ間違えて回答してしまいました。 syncは、キューに同期処理を追加するので、キューに追加された処理が終わるまでそこで処理が停止します。ですから、 1: i = 0の時のクロージャーをグローバルキューに積み、クロージャー内の処理が終わるまで待つ 2: (4)を実行する 3: i = 1の時のクロージャーをグローバルキューに積み、クロージャー内の処理が終わるまで待つ 4: (4)を実行する 5: i = urlArray.countになるまで繰り返し となります。結果的に、DispatchQuereを使わずにプログラムを書くのと同じになります(クロージャーの処理が完了するまで、次の処理には移りません)。  さて、私はAlamofireを知らないのですが、関数scrapWebsite()の中のAlamofire.request(url).responseStringという関数が非同期処理を使っていそうです。ですから、scrapWebsite()を呼び出して帰ってきた時点では、Alamofire.request(url).responseStringの処理が終わっていない、すなわち、playersの値が更新されていないのではないでしょうか。 players.count3, players.count2, players.count1の順番に表示されるのが理想だけれども、実際はcount3がcount2より先に表示されていませんか?
kyokio

2020/02/21 14:43

ありがとうございます。 だんだん理解してきました。大きく分けると並列or直列処理、同期or非同期の組み合わせが選べるんですね。 今回はループのたびにqueueをに同期処理を追加して追加されたすべての処理が終わるまで待っている、かつ並列処理だが処理にかかる時間が(1),(2),(3)でさがあまりないため処理を開始した順番に処理が終わり、結果的にDispatchQueueを使わない場合と同じになる。 `Alamofire.requesr(url).responseString`という関数が非同期処理を行なっており、処理に時間がかかるため最後に実行されているということでしょうか? count3がcount1より先に表示されています。 まだmainキューやスレッドがどのように動作してるのか理解してないのでもう少し調べてみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問