傻瓜式 dataTaskWithURL
dataTaskWithURL for dummies
我一直在学习 iDev,但我仍然无法处理 http 请求。
这似乎很疯狂,但我谈论同步请求的每个人都不理解我。好的,尽可能保持后台队列以提供流畅的 UI 非常重要。但在我的例子中,我从服务器加载 JSON 数据,我需要立即使用这些数据。
我实现它的唯一方法是信号量。可以吗?或者我必须使用其他东西?我尝试了 NSOperation,但实际上我有很多小请求,所以为我创建每个 class 似乎不是容易阅读的代码。
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
}
不要试图强制网络连接同步工作。它总是会导致问题。进行上述调用的任何代码都可能被阻止长达 90 秒(30 秒 DNS 超时 + 60 秒请求超时),等待该请求完成或失败。那是永恒。如果该代码在 iOS 上的主线程上 运行,操作系统将在您到达 90 秒标记之前很久就彻底终止您的应用程序。
相反,将您的代码设计为异步处理响应。基本上:
- 创建数据结构来保存各种请求的结果,例如从用户那里获取信息。
- 启动这些请求。
- 当每个请求返回时,检查您是否拥有做某事所需的所有数据,然后再做。
举一个非常简单的例子,如果你有一个方法用登录用户的名字更新 UI,而不是:
[self updateUIWithUserInfo:[self getUserInfoForUser:user]];
您可以将其重新设计为:
[self getUserInfoFromServerAndRun:^(NSDictionary *userInfo) {
[self updateUIWithUserInfo:userInfo];
}];
以便当对请求的响应到达时,它会执行 UI 更新操作,而不是尝试启动 UI 更新操作并阻止它等待来自服务器的数据。
如果您需要两件事——比如用户信息和用户已阅读的书籍列表,您可以这样做:
[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;
...
}
或其他什么。积木在这里是你的朋友。 :-)
是的,重新考虑异步工作的代码很痛苦,但最终结果要可靠得多,并且会产生更好的用户体验。
你写别人不理解你,但另一方面也暴露了你不理解异步网络请求是如何工作的
例如,假设您正在为特定时间设置闹钟。
现在您有两种选择来度过接下来的时间。
- 什么都不做,坐在闹钟前等待闹钟响起。你做过吗?当然不是,但这正是您对网络请求的看法。
- 忽略闹钟直到它响起,做一些有用的事情。这就是异步任务的工作方式。
就编程语言而言,您需要一个完成处理程序,在加载数据时由网络请求调用。在 Swift 中,您为此目的使用了闭包。
为方便起见,为 success
和 failure
情况声明一个具有关联值的枚举,并将其用作完成处理程序中的 return 值
enum RequestResult {
case Success(User), Failure(Error)
}
向您的函数添加一个完成处理程序,包括错误情况。 强烈建议始终处理异步任务的错误参数。 当数据任务 returns 它调用完成闭包传递 user
或 error
视情况而定。
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
}
}
实际上,您 semaphore
之后的时间点与完成块中的 switch result
行完全相同。
永远不要使用信号量作为不处理异步模式的借口
我希望闹钟示例能够阐明异步数据处理的工作原理,以及为什么获得通知(主动)比等待(被动)更有效。
我一直在学习 iDev,但我仍然无法处理 http 请求。 这似乎很疯狂,但我谈论同步请求的每个人都不理解我。好的,尽可能保持后台队列以提供流畅的 UI 非常重要。但在我的例子中,我从服务器加载 JSON 数据,我需要立即使用这些数据。 我实现它的唯一方法是信号量。可以吗?或者我必须使用其他东西?我尝试了 NSOperation,但实际上我有很多小请求,所以为我创建每个 class 似乎不是容易阅读的代码。
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
}
不要试图强制网络连接同步工作。它总是会导致问题。进行上述调用的任何代码都可能被阻止长达 90 秒(30 秒 DNS 超时 + 60 秒请求超时),等待该请求完成或失败。那是永恒。如果该代码在 iOS 上的主线程上 运行,操作系统将在您到达 90 秒标记之前很久就彻底终止您的应用程序。
相反,将您的代码设计为异步处理响应。基本上:
- 创建数据结构来保存各种请求的结果,例如从用户那里获取信息。
- 启动这些请求。
- 当每个请求返回时,检查您是否拥有做某事所需的所有数据,然后再做。
举一个非常简单的例子,如果你有一个方法用登录用户的名字更新 UI,而不是:
[self updateUIWithUserInfo:[self getUserInfoForUser:user]];
您可以将其重新设计为:
[self getUserInfoFromServerAndRun:^(NSDictionary *userInfo) {
[self updateUIWithUserInfo:userInfo];
}];
以便当对请求的响应到达时,它会执行 UI 更新操作,而不是尝试启动 UI 更新操作并阻止它等待来自服务器的数据。
如果您需要两件事——比如用户信息和用户已阅读的书籍列表,您可以这样做:
[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;
...
}
或其他什么。积木在这里是你的朋友。 :-)
是的,重新考虑异步工作的代码很痛苦,但最终结果要可靠得多,并且会产生更好的用户体验。
你写别人不理解你,但另一方面也暴露了你不理解异步网络请求是如何工作的
例如,假设您正在为特定时间设置闹钟。
现在您有两种选择来度过接下来的时间。
- 什么都不做,坐在闹钟前等待闹钟响起。你做过吗?当然不是,但这正是您对网络请求的看法。
- 忽略闹钟直到它响起,做一些有用的事情。这就是异步任务的工作方式。
就编程语言而言,您需要一个完成处理程序,在加载数据时由网络请求调用。在 Swift 中,您为此目的使用了闭包。
为方便起见,为 success
和 failure
情况声明一个具有关联值的枚举,并将其用作完成处理程序中的 return 值
enum RequestResult {
case Success(User), Failure(Error)
}
向您的函数添加一个完成处理程序,包括错误情况。 强烈建议始终处理异步任务的错误参数。 当数据任务 returns 它调用完成闭包传递 user
或 error
视情况而定。
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
}
}
实际上,您 semaphore
之后的时间点与完成块中的 switch result
行完全相同。
永远不要使用信号量作为不处理异步模式的借口
我希望闹钟示例能够阐明异步数据处理的工作原理,以及为什么获得通知(主动)比等待(被动)更有效。