28624


Swiftにて以下のようなコードを書いたところ、関数bのprintでは1が出力されるものの、関数aのprintでは0のまま出力されます。  
どうすれば関数a内の変数xに関数bでの代入を反映させられるのでしょうか？

[code]

    class ViewController: UIViewController {
    
     var x = 0
    
     func a(){
      b()
      print(x)
    }
    
     func b() {
      x = 1
      print(x)
    }
    
[/code]

追記  
説明を端折ってしまい、すみません。  
メソッドa,bは、正確には以下のコードです。

[code]

    @IBAction func a(sender: UIBarButtonItem) {        
        b()
        print(self.id) // 2
    
        // 画像をNSDataに変換
        let fileName = "image\(self.id).png"
        let pngData = NSData(data: UIImagePNGRepresentation(self.displayImage.image!)!)
        let file = NCMBFile.fileWithName(fileName, data: pngData) as! NCMBFile
    
        // ファイルストアへ画像のアップロード
        file.saveInBackgroundWithBlock({ (error: NSError!) -> Void in
            if error != nil {
                // 保存失敗時の処理
    
            } else {
                // 保存成功時の処理
    
            }
        })
    
      }
    
    func b()  {
        // user1クラスを検索するNCMBQueryを作成
        let query = NCMBQuery(className:"xxx")
    
        // データストアでの検索を行う
        query.findObjectsInBackgroundWithBlock{(objects, error ) in
            if error != nil {
                // 検索に失敗した場合の処理
            }else{
                // 検索に成功した場合の処理
                self.id = objects.count
                print(self.id) //1
            }
        }
    }
    
[/code]

print1ではobjects.countの値が出力されましたが、2でははじめに定義した0がそのまま出力されました。また、アップロードされた画像のナンバリングも、objects.countの値ではなく、0のままでした。


CC BY-SA 3.0
2016-09-01T05:34:56.247
28620
2016-09-01T11:56:00.253
2016-09-01T11:08:15.077
18443
18443
question
swift
Swift:関数内で変数の値を更新して、他の関数で使いたい


(前半のコードを残しておいても、あまり多くの人に役立ちそうにないので、ばっさりカットして、 **追記** 以降の話に絞り込みたいと思います。)

あなたが体験しておられる事象は「非同期処理」に関わるものです。(ここ日本語版スタック・オーバーフローにも関連の質問はすでにたくさん上がっているのですが、検索で見つけ出すのは難しいかもしれません。)

`NCMBQuery`というのはNifty
Cloudで用意されているものだそうで、私自身は使った事がないのですが、重要なのは、そのAPIは非同期で実行されるということです。

[code]

    [b()の中にて]
    
    (あなたのアプリ)済んだら、これ`{(objects, error ) in...//1...}`しといてな
    (NCMB)よっしゃ(と言って「これ」をどっかに仕舞い込んだら、一旦処理を終了する)
    
    [b()から戻った直後]
    
    (あなたのアプリ)[まだ「済んだら」のタイミングが来る前に`print(self.id) //2`を実行]
    -> 検索は終わっていないので、`print(self.id)`は「これ」が実行される前の値
    [その後もまだ設定されていない`self.id`を使おうとするが...]
    
    [その後の未来のどこか、いつしか検索が終わった...]
    
    (NCMB)検索が終わったんで「これ」実行しとくわな
    -> //1の`print(self.id)`が実行される
    
    [せっかく`self.id`に値を入れたものの誰も使うものがない...]
    
[/code]

ブレークポイントなどを使わずに、どこの`print(self.id)`が実行されたのかを追っていけば、上記の順番で実行されるのがわかるはずです。(例えば`print("#1",self.id)`,
`print("#2",self.id)`としてみる。)

* * *

これに対応するためには、どこかのスレッドにあった「delegate内だけで有効な変数を取り扱う」のと同じような発想が求められます。

[1] 完了ハンドラーの中でインスタンス変数に値を設定し、UIの更新などでユーザに伝える

[2] 完了ハンドラーの中でその結果を使うメソッドを呼ぶ

他に、[3]
あなたのメソッド用の完了ハンドラーをパラメータとして受け取りそれを(NCMBQueryの)完了ハンドラーの中から呼び出す、という手もあるのですが、1レベル上級になるなので、[2]の方針であなたのコードを書き直してみると、こんな感じになります。

[code]

    @IBAction func a(sender: UIBarButtonItem) {
        b()
        //b()を呼び出したら何もせずに処理を終了する、完了した時の処理は別メソッドに任せる
    }
    
    func b()  {
        // user1クラスを検索するNCMBQueryを作成
        let query = NCMBQuery(className:"xxx")
    
        // データストアでの検索を行う
        query.findObjectsInBackgroundWithBlock{(objects, error ) in
            if error != nil {
                // 検索に失敗した場合の処理
            }else{
                // 検索に成功した場合の処理
                self.id = objects.count
                print(self.id) //1
                //検索に成功して受け取った値は必ず検索の完了ハンドラーの中で使う
                //処理がやや複雑なら別メソッドにするとわかりやすい
                doSomethingWith(self.id)
            }
        }
    }
    
    //`b()`の中で`findObjectsInBackgroundWithBlock`が完了した時に呼ぶ処理は別メソッドに外出しする
    func doSomethingWith(id: Int) {
        print(self.id) // 2
    
        // 画像をNSDataに変換
        let fileName = "image\(id).png"
        let pngData = NSData(data: UIImagePNGRepresentation(self.displayImage.image!)!)
        let file = NCMBFile.fileWithName(fileName, data: pngData) as! NCMBFile
    
        // ファイルストアへ画像のアップロード
        file.saveInBackgroundWithBlock({ (error: NSError!) -> Void in
            if error != nil {
                // 保存失敗時の処理
            } else {
                // 保存成功時の処理
            }
        })
    }
    
[/code]

* * *

私的には非同期処理をうまく使うコツは『済んだら「これ」しといてな』感なのですが、もしかしたらかえって分かりにくくしてしまったかもしれません。お試しいただいて何かあれば、ご質問を編集するかコメントでおしらせください。


CC BY-SA 3.0
2016-09-01T10:20:59.633
28624
2016-09-01T11:56:00.253
2016-09-01T11:56:00.253
13972
13972
28620
answer