如何在移动到下一行代码之前完成 NSURLSession 请求

Posted

技术标签:

【中文标题】如何在移动到下一行代码之前完成 NSURLSession 请求【英文标题】:How to finish NSURLSession request before moving to next line of code 【发布时间】:2015-11-03 02:00:35 【问题描述】:

任何帮助都将不胜感激,我有一堆用于调用 NSURLConnection Synchronous 方法的类,但不再存在。如何以在继续前等待响应的方式调用此代码块?

class func login(email:String,password:String)->Int
    var userId = 0
    let postEndpoint:String = "https://www.testsite.com/user/login.php"
    let url = NSURL(string: postEndpoint)!
    let urlRequest = NSMutableURLRequest(URL: url, cachePolicy: .ReloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 15.0)
    urlRequest.HTTPMethod = "POST"
    let body = "email=\(email)&password=\(password)".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
    urlRequest.HTTPBody = body
    let task = NSURLSession.sharedSession().dataTaskWithRequest(urlRequest) (data, response, error) in
        if data != "" && error == nil
            do 
                let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as! [String: AnyObject]
                if let id = json["id"] as? Int 
                    print("found id:\(id)")
                    userId = id
                                
             catch let error as NSError 
                print("Failed to load: \(error.localizedDescription)")
                userId = 2
            

        
        else
            userId = 2
        
    
    task.resume()
    return userId

调用这个的代码...

success = User.login(emailTextField.text!, password: passwordTextField.text!)
print("success last: \(success)")

问题是在返回用户 ID 之前调用了“最后一次成功:”。啊啊啊啊啊啊!

【问题讨论】:

你不能做你想做的事。 login 将在 dataTaskWithRequest(urlRequest) 完成处理程序运行之前完成。这就是异步的意思!正确的方法是传入你自己的完成处理程序,并在另一个完成处理程序的末尾调用它。例如,请参阅我的答案:***.com/a/31064653/341994 你不能,但你可以使用NSTimerNSURLSession委托方法在某些数据传输后运行函数。 【参考方案1】:

几年前我问过同样的问题。我没有收到那个答案。那时人们制造的 cmets 和现在一样。我确实认为完成处理程序是一个更好的主意,但是现在您使用的是 Swift,您可以使用 Grand Central Dispatch 来做到这一点:

class func login(email:String,password:String)->Int
    var userId = 0
    let postEndpoint:String = "https://www.testsite.com/user/login.php"
    let url = NSURL(string: postEndpoint)!
    let urlRequest = NSMutableURLRequest(URL: url, cachePolicy: .ReloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 15.0)
    urlRequest.HTTPMethod = "POST"
    let body = "email=\(email)&password=\(password)".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
    urlRequest.HTTPBody = body

    let group = dispatch_group_create()

    // Signal the beginning of the async task. The group won't
    // finish until you call dispatch_group_leave()
    dispatch_group_enter(group)

    dispatch_group_async(group, dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)) 
        let task = NSURLSession.sharedSession().dataTaskWithRequest(urlRequest) (data, response, error) in
            if data != "" && error == nil
                do 
                    let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as! [String: AnyObject]
                    if let id = json["id"] as? Int 
                        print("found id:\(id)")
                        userId = id
                    
                 catch let error as NSError 
                    print("Failed to load: \(error.localizedDescription)")
                    userId = 2
                
            
            else 
                userId = 2
            

            // Signal the end of the async task
            dispatch_group_leave(group)
        
        task.resume()
    

    // Wait 30 seconds for the group to be done
    let timeout = dispatch_time(DISPATCH_TIME_NOW, 30_000_000_000)
    dispatch_group_wait(group, timeout)

    return userId

如果您不希望它超时,请将倒数第二行替换为:

dispatch_group_wait(group, DISPATCH_TIME_FOREVER)

这有什么用处:

假设您必须调用 3 个 Web 服务。在所有 3 个都完成之前,您的申请无法继续。您可以调用第一个服务,然后在其完成块中调用第二个服务,依此类推。但是,您需要为等待付出时间代价。

使用此模式,您可以同时调用所有 3 个:

let group = dispatch_group_create()
dispatch_group_enter(group)
dispatch_group_enter(group)
dispatch_group_enter(group)

dispatch_group_async(group, dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)) 
    let task1 = NSURLSession.sharedSession().dataTaskWithRequest(request1) ...
        // handle response & error
        dispatch_group_leave()
    

    let task2 = NSURLSession.sharedSession().dataTaskWithRequest(request2) ...
        // handle response & error
        dispatch_group_leave()
    

    let task3 = NSURLSession.sharedSession().dataTaskWithRequest(request3) ...
        // handle response & error
        dispatch_group_leave()
    


let timeout = dispatch_time(DISPATCH_TIME_NOW, 30_000_000_000)
dispatch_group_wait(group, timeout)

// proceed with your code...

【讨论】:

我明天有时间试一试,然后告诉你进展如何。我是自学成才,不要全职这样做,我非常感谢您的帮助。我花了一些时间试图让完成处理程序工作,但时间仍然存在问题。我将不得不更好地学习这些。无论如何,非常感谢!一点点善意大有帮助。 调度组工作得很好。我将继续关注完成处理程序,并在我拨入它们后使用它们,谢谢!

以上是关于如何在移动到下一行代码之前完成 NSURLSession 请求的主要内容,如果未能解决你的问题,请参考以下文章

在移动到下一个活动之前完整地运行一个函数

Perl - 需要在上一行完成之前继续下一行

等待函数首先执行,然后再移动到下一行代码

C语言怎么在输入完成后把光标移到下一行开始处

如何在移动到下一个文本框之前强制用户输入有效的电子邮件?

如何阻止用户移动到下一步,直到使用 Angular Material Stepper(Angular 4)完成当前步骤