

下記の関数は、バックグラウンド処理の関係で、動作順序が変わってしまい、関数の返り値が、予定したものにならない。

[code]

    let task = NSURLSession.sharedSession().dataTaskWithRequest.....
    
[/code]

この行以降が飛ばされ、関数が終了する。

改善方法を教えていただけないでしょうか？  
Postを利用するため、この方法になっています。代替案でもいいです。

コードが古すぎていたので、HTTPアドレス以外、正しく動くように修正いたしました。

下記の場合、printの出力順が、"NG"->HTMLコードとなる。正しくはHTMLコード->"OK"となってほしい。

よろしく、お願いいたします。

[code]

    import UIKit
    
    class ViewController: UIViewController ,NSURLSessionDelegate, NSURLSessionDataDelegate {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    
        print(Test())
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    
    func Test()-> String{
        let post = "name=Masuda&addr=Tokyo"
        let postdata = post.dataUsingEncoding(NSShiftJISStringEncoding)!
        let url = NSURL(string: "http://moonmile.net/ios9/post.php")
        let req = NSMutableURLRequest(URL: url!)
        req.HTTPMethod = "POST"
        req.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
        let length = postdata.length
        req.setValue("\(length)", forHTTPHeaderField: "Content-Length")
        req.HTTPBody = postdata
        var textBuf : String = "NG"
        let task = NSURLSession.sharedSession().dataTaskWithRequest(req, completionHandler: {
            ( data, res, err ) in
            if data != nil {
    
                let text = NSString(data: data!, encoding: NSShiftJISStringEncoding)
    
                print(NSString(data: data!, encoding: NSShiftJISStringEncoding))
                dispatch_async(dispatch_get_main_queue(), {
                    textBuf = "OK"
                })
            }
        })
    
        task.resume()
        return textBuf
    }
    }
    
[/code]


CC BY-SA 3.0
2016-01-08T02:08:26.047
20728
2016-01-17T04:19:46.470
2016-01-17T04:19:46.470
10845
10845
question
ios swift xcode7
HTTP通信で処理がバックグラウンドに移行してしまう為、データを正しく取得できないのを改善したい。代替案でも?


以下のコードが解決後のコードです。

[code]

    import UIKit
    
    class ViewController: UIViewController ,NSURLSessionDelegate, NSURLSessionDataDelegate {
    
        var waiting = true //追加
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
    
            print(Test())
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    
    
        func Test()-> String{
            let post = "name=Masuda&addr=Tokyo"
            let postdata = post.dataUsingEncoding(NSShiftJISStringEncoding)!
            let url = NSURL(string: "http://moonmile.net/ios9/post.php")
            let req = NSMutableURLRequest(URL: url!)
            req.HTTPMethod = "POST"
            req.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
            let length = postdata.length
            req.setValue("\(length)", forHTTPHeaderField: "Content-Length")
            req.HTTPBody = postdata
            var textBuf : String = "NG"
            let task = NSURLSession.sharedSession().dataTaskWithRequest(req, completionHandler: {
                ( data, res, err ) in
                if data != nil {
    
                    let text = NSString(data: data!, encoding: NSShiftJISStringEncoding)
    
                    print(NSString(data: data!, encoding: NSShiftJISStringEncoding))
                    dispatch_async(dispatch_get_main_queue(), {
                        textBuf = "OK"
                        waiting = false //追加
                    })
                }
            })
    
            task.resume()
            //追加
            while (waiting) {
                NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate())
            }
            return textBuf
        }
    }
    
[/code]


CC BY-SA 3.0
2016-01-09T23:47:20.187
20791
2016-01-09T23:47:20.187
10845
20728
answer


非同期処理の結果がすぐに得られないからといって、「終了待ちをする」というのは誤った解決方法です。単純ループで終了待ちをするのはもってのほかですが、今回の例のようにメインスレッドから呼ばれる想定のメソッド内で不特定の時間待ち続けると、メインスレッドの処理が停滞して、ユーザビリティーを損ないますので、絶対にやめてください。

非同期処理を利用するコードを書く場合には、以下の原則を覚えておくと良いでしょう。  
1\. 非同期処理を起動するメソッドからは処理の結果は返せない  
2\. 非同期処理を起動したら、そのメソッドの中ではもう何もしない  
3\. 非同期処理の結果は必ず完了ハンドラーの中で処理する

実際のアプリの場合には、単にStringの中身をprintするだけ、ということは考えられないので、汎用のメソッドにしたいと言うことであれば、結果を戻り値として返すのではなく、結果を引数として渡すような完了ハンドラーを自前のメソッドにも持たせるというのが一つの方法です。

ご提示いただいた元の例をそのように書き直してみると、こんな感じになります。

[code]

    import UIKit
    
    class ViewController: UIViewController ,NSURLSessionDelegate, NSURLSessionDataDelegate {
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
    
            Test({(result: String)->Void in //非同期処理の結果は必ず完了ハンドラーの中で処理する
                print(result)
            })
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    
        func Test(completionHandler: (String)->Void) { //自前のメソッドにも完了ハンドラーを持たせる
            let post = "name=Masuda&addr=Tokyo"
            let postdata = post.dataUsingEncoding(NSShiftJISStringEncoding)!
            let url = NSURL(string: "http://moonmile.net/ios9/post.php")
            let req = NSMutableURLRequest(URL: url!)
            req.HTTPMethod = "POST"
            req.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
            //HTTPBodyをNSDataで指定する場合、Content-Lengthの設定は不要
            req.HTTPBody = postdata
            var textBuf : String = "NG"
            let task = NSURLSession.sharedSession().dataTaskWithRequest(req, completionHandler: {
                ( data, res, err ) in
                if data != nil {
    
                    let text = String(data: data!, encoding: NSShiftJISStringEncoding)
    
                    print(text)
                    dispatch_async(dispatch_get_main_queue(), {
                        textBuf = "OK"
                        completionHandler(textBuf) //完了ハンドラーの中で自前の完了ハンドラーを呼ぶ
                    })
                }
            })
            task.resume()
            //非同期処理を起動した後は、そのメソッドの中では何もしない
        }
    }
    
[/code]

ボタンが押された後に何かの処理をする、という場合に`while !isButtonPressed
{/*...*/}`といった具合にループでボタンが押されるのを待つと言った書き方は(iOS等のプログラムでは)見たことがないと思います。(大昔のワンボードマイコンのプログラミングでは普通にやってましたが…。)通信を伴う非同期処理の場合にも、処理を待ち合わせてつじつまを合わせるのではなく、きちんとした非同期処理のパターンを身につけてください。


CC BY-SA 3.0
2016-01-11T09:28:14.730
20830
2016-01-11T09:33:22.120
2016-01-11T09:33:22.120
13972
13972
20728
answer