dataTaskWithURL for dummies

时间:2016-09-09 04:36:37

标签: semaphore nsurlsession swift3

我一直在学习iDev,但我还是无法处理http请求。 这似乎很疯狂,但我谈论同步请求的每个人都不理解我。好吧,尽可能保持后台队列以提供流畅的UI非常重要。但在我的情况下,我从服务器加载JSON数据,我需要立即使用这些数据。 我实现它的唯一方法是信号量。好吗?或者我必须使用其他?我尝试了NSOperation,但事实上我需要很多小的请求所以为我创建每个类似乎不是简单的阅读代码。

func getUserInfo(userID: Int) -> User {
    var user = User()
    let linkURL = URL(string: "https://server.com")!
    let session = URLSession.shared
    let semaphore = DispatchSemaphore(value: 0)
    let dataRequest = session.dataTask(with: linkURL) { (data, response, error) in
        let json = JSON(data: data!)
        user.userName = json["first_name"].stringValue
        user.userSurname = json["last_name"].stringValue
        semaphore.signal()
    }
    dataRequest.resume()
    semaphore.wait(timeout: DispatchTime.distantFuture)
    return user
}

2 个答案:

答案 0 :(得分:1)

您写道,人们不了解您,但另一方面,它表明您不了解异步网络请求的工作原理。

例如,假设您在特定时间设置闹钟。

现在您有两种选择可以花费以下时间。

  1. 除了坐在闹钟前面什么都不做,等到闹钟发生。你有没有做过?当然不是,但这正是您对网络请求的想法。
  2. 做几件有用的事情,忽略闹钟,直到它响起。这就是异步任务的工作方式。
  3. 就编程语言而言,您需要一个完成处理程序,在加载数据时由网络请求调用。在Swift中,你正在使用一个闭包。

    为方便起见,声明一个带有successfailure个案例的关联值的枚举,并将其用作完成处理程序中的返回值

    enum RequestResult {
      case Success(User), Failure(Error)
    }
    

    为您的函数添加一个完成处理程序,包括错误情况。 强烈建议始终处理异步任务的错误参数。当数据任务返回时,调用完成闭包,根据情况通过usererror

    func getUserInfo(userID: Int, completion:@escaping (RequestResult) -> ())  {
    
      let linkURL = URL(string: "https://server.com")!
      let session = URLSession.shared
      let dataRequest = session.dataTask(with: linkURL) { (data, response, error) in
        if error != nil {
          completion(.Failure(error!))
        } else {
    
          let json = JSON(data: data!)
          var user = User()
          user.userName = json["first_name"].stringValue
          user.userSurname = json["last_name"].stringValue
          completion(.Success(user))
        }
      }
      dataRequest.resume()
    }
    

    现在您可以使用以下代码调用该函数:

    getUserInfo(userID: 12) { result in
      switch result {
      case .Success(let user) :
        print(user)
        // do something with the user
    
      case .Failure(let error) :
        print(error)
        // handle the error
      }
    }
    

    在实践中,完成版块中semaphoreswitch result行之后的时间点完全相同。

    永远不要使用信号量作为不在处理异步模式的不在场证明

    我希望闹钟示例阐明异步数据处理的工作原理以及为什么获得通知(主动)而非等待(被动)的效率要高得多。

答案 1 :(得分:-1)

请勿尝试强制网络连接同步工作。它总会导致问题。无论什么代码进行上述调用都可能被阻止长达90秒(30秒DNS超时+ 60秒请求超时),等待该请求完成或失败。那是永恒的。如果该代码在iOS上的主线程上运行,操作系统将在您达到90秒标记之前很久就终止您的应用程序。

相反,设计代码以异步处理响应。基本上是:

  • 创建数据结构以保存各种请求的结果,例如从用户获取信息。
  • 启动这些请求。
  • 当每个请求返回时,检查您是否拥有执行某些操作所需的所有数据,然后执行此操作。

对于一个非常简单的示例,如果您有一个方法可以使用登录用户的名称更新UI,而不是:

[self updateUIWithUserInfo:[self getUserInfoForUser:user]];

你会将其重新设计为:

[self getUserInfoFromServerAndRun:^(NSDictionary *userInfo) {
    [self updateUIWithUserInfo:userInfo];
}];

这样当对请求的响应到达时,它会执行UI更新操作,而不是尝试启动UI更新操作并阻止它等待来自服务器的数据。

如果您需要两件事 - 比如userInfo和用户已阅读的书籍列表,您可以这样做:

[self getUserInfoFromServerAndRun:^(NSDictionary *userInfo) {
    self.userInfo = userInfo;
    [self updateUI];
}];
[self getBookListFromServerAndRun:^(NSDictionary *bookList) {
    self.bookList = bookList;
    [self updateUI];
}];
...
(void)updateUI
{
    if (!self.bookList) return;
    if (!self.userInfo) return;

    ...
}

或其他什么。街区是你的朋友。 : - )

是的,重新考虑您的代码以异步方式工作是一件痛苦的事情,但最终结果更加可靠,并且可以带来更好的用户体验。