在启动我的应用程序时,我会执行一些http请求,一些沉重的http请求(下载一些图像)和一些UIGraphics的繁重任务(例如,使用GraphicsContext从两个UIImages和其他操作中为GMSMarker执行图标)。这需要一些时间,所以我想同时完成所有这些任务。你能告诉我最好的方法吗? 一开始我必须:
下载并写入本地数据库所有职位
下载设备,用户和地理围栏的图片
为设备,用户和地理围栏设置GMSMarkers(在该对象的图像可用后 - 用于设置标记图标)
我的登录功能代码(虽然有效,但速度太慢):
func loginPressed(_ sender: UIButton) {
guard
let username = self.usernameTextField.text,
let password = self.passwordTextField.text,
!username.isEmpty,
!password.isEmpty
else {
return
}
self.loginButton.isEnabled = false
self.activityIndicator.startAnimating()
WebService.shared.connect(email: username, password: password) { error, loggedUser in
guard
error == nil,
let loggedUser = loggedUser
else {
self.showAlert(title: "Ошибка подключения", message: error?.localizedDescription ?? "", style: .alert)
self.activityIndicator.stopAnimating()
self.loginButton.isEnabled = true
return
}
DB.users.client.insert(loggedUser)
print("Start loading user photo...")
loggedUser.loadPhoto() { image in
if let image = image {
loggedUser.photo = UIImageJPEGRepresentation(image, 0.0)
}
print("User photo loaded...")
loggedUser.marker = UserMarker(loggedUser, at: CLLocation(latitude: 48.7193900, longitude: 44.50183))
DB.users.client.modify(loggedUser)
}
DB.geofences.server.getAll() { geofences in
DB.devices.server.getAll() { devices in
DB.positions.server.getAll() { positions in
for device in devices {
device.loadPhoto() { image in
if let image = image {
device.photo = UIImageJPEGRepresentation(image, 0.0)
}
if let position = positions.findById(device.positionId) {
device.marker = DeviceMarker(device, at: position)
}
device.attributes.battery = device.lastKnownBattery(in: positions)
}
}
geofences.forEach({$0.marker = GeofenceMarker($0)})
DB.geofences.client.updateAddress(geofences) { geofences in
if DEBUG_LOGS {
print("Geofences with updated addresses: ")
geofences.forEach({print("\($0.name), \($0.address ?? "")")})
}
DB.devices.client.insert(devices)
DB.geofences.client.insert(geofences)
DB.positions.client.insert(positions)
self.activityIndicator.stopAnimating()
WebService.shared.addObserver(DefaultObserver.shared)
self.performSegue(withIdentifier: "toMapController", sender: self)
}
}
}
}
}
}
不确定在这里发布所有类和对象的代码片段是个好主意,希望你能理解。 任何帮助,将不胜感激。
P.S。如果你想知道什么是DB,它的数据库,它由两部分组成 - 每组对象的服务器端和客户端,所以第一个任务是从服务器获取所有对象并将它们写入内存(在客户端数据库中)
P.S。我已经将登录时的“下载所有内容”改为“立即下载所有我需要的内容并稍后下载休息”。所以现在我拥有所有设备,地理围栏和位置后我正在执行MapController,我将在其上显示所有这些对象。刚登录后我正在使用默认的iconView显示deviceMarkers(GMSMarker)。问题是 - 我可以在显示所有对象的地图之后开始下载背景中的设备照片,然后用那些照片刷新标记(当然是在主线程中)吗?
答案 0 :(得分:0)
默认情况下,网络请求是异步的,因此您要求的已经是预期的行为。
然而,您可以使用诸如then之类的Promises库让您的生活更加简单。
示例用法可能如下所示:
login(email: "foo@bar.com", password: "pa$$w0rd")
.whenAll(syncDevices(), syncGeofences(), syncUsers(), syncPositions(), syncImages())
.onError { err in
// process error
}
.finally {
// update UI
}
答案 1 :(得分:0)
您的登录功能很慢,因为您正在下载并写入磁盘(正如您在问题中所提到的)“所有”闭包中的数据(地理围栏,设备和/或位置)。此外,所有操作都在主线程中执行。您绝不应该在主线程中执行I / O(联网,写入磁盘),因为此线程主要用于UI更新。您应该使用GCD将昂贵的任务卸载到另一个线程。
此外,值得一提的是,写入磁盘的操作相对较慢,特别是如果您正在为正在下载的每个项目执行此操作。
我建议您只需下载要显示的数据,然后在DispatchQueue(GCD)中使用异步任务,以便在UI中显示数据后将数据下载到磁盘。
我不确定DB.geofences.server.getAll()
行对地理围栏,设备和位置(关于如何处理网络或数据库提取)的作用,所以我不能就此提出建议。我可以给你的建议是,按照以下方式构建你的代码:
.UserInitiated
优先级在另一个线程上异步通过网络调用获取数据(以快速获得结果)。DispatchQueue.main.async { ... }
使用您刚获得的数据在主线程中异步更新UI。如果我上面说的任何内容对你没有任何意义,请阅读AppCoda关于GCD here和RayWenderlich的GCD文章here的文章。他们将为您提供有关iOS中GCD的基本知识。完成后,请回过头来尝试按照我上面推荐的方式构建代码。
我希望这有帮助!