Поскольку этот поток немного разветвился в общем обсуждении текущих реализаций с плавающей запятой, я бы добавил, что есть проекты по исправлению их проблем.
Взгляните на https: // posithub.org/, который демонстрирует тип номера, называемый posit (и его предшественник unum), который обещает предложить лучшую точность с меньшим количеством бит. Если мое понимание верное, оно также фиксирует проблемы в вопросе. Весьма интересный проект, человек, стоящий за ним, является математиком Dr. Джон Густафсон . Все это с открытым исходным кодом, с множеством реализаций в C / C ++, Python, Julia и C # ( https://hastlayer.com/arithmetics ).
Вы можете использовать группы отправки для запуска асинхронного обратного вызова, когда все ваши запросы завершатся.
Вот пример в Swift 4.1 (также работает в Swift 3), используя группы отправки выполняйте обратный вызов асинхронно, когда все сетевые запросы завершены.
override func viewDidLoad() {
super.viewDidLoad()
let myGroup = DispatchGroup()
for i in 0 ..< 5 {
myGroup.enter()
Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
print("Finished request \(i)")
myGroup.leave()
}
}
myGroup.notify(queue: .main) {
print("Finished all requests.")
}
}
Выход
Finished request 1
Finished request 0
Finished request 2
Finished request 3
Finished request 4
Finished all requests.
Для тех, кто использует более ранний Swift 2.3, вот пример, используя его синтаксис :
override func viewDidLoad() {
super.viewDidLoad()
let myGroup = dispatch_group_create()
for i in 0 ..< 5 {
dispatch_group_enter(myGroup)
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
print("Finished request \(i)")
dispatch_group_leave(self.myGroup)
}
}
dispatch_group_notify(myGroup, dispatch_get_main_queue(), {
print("Finished all requests.")
})
}
Xcode 8.3.1 - Swift 3
Это принятый ответ paulvs, преобразованный в Swift 3:
let myGroup = DispatchGroup()
override func viewDidLoad() {
super.viewDidLoad()
for i in 0 ..< 5 {
myGroup.enter()
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
print("Finished request \(i)")
myGroup.leave()
}
}
myGroup.notify(queue: DispatchQueue.main, execute: {
print("Finished all requests.")
})
}
Группа отправки хороша, но порядок отправленных запросов случайный.
Finished request 1
Finished request 0
Finished request 2
В моем случае проект, каждый запрос, который необходимо запустить, - это правильный порядок. Если это может кому-то помочь:
public class RequestItem: NSObject {
public var urlToCall: String = ""
public var method: HTTPMethod = .get
public var params: [String: String] = [:]
public var headers: [String: String] = [:]
}
public func trySendRequestsNotSent (trySendRequestsNotSentCompletionHandler: @escaping ([Error]) -> () = { _ in }) {
// If there is requests
if !requestItemsToSend.isEmpty {
let requestItemsToSendCopy = requestItemsToSend
NSLog("Send list started")
launchRequestsInOrder(requestItemsToSendCopy, 0, [], launchRequestsInOrderCompletionBlock: { index, errors in
trySendRequestsNotSentCompletionHandler(errors)
})
}
else {
trySendRequestsNotSentCompletionHandler([])
}
}
private func launchRequestsInOrder (_ requestItemsToSend: [RequestItem], _ index: Int, _ errors: [Error], launchRequestsInOrderCompletionBlock: @escaping (_ index: Int, _ errors: [Error] ) -> Void) {
executeRequest(requestItemsToSend, index, errors, executeRequestCompletionBlock: { currentIndex, errors in
if currentIndex < requestItemsToSend.count {
// We didn't reach last request, launch next request
self.launchRequestsInOrder(requestItemsToSend, currentIndex, errors, launchRequestsInOrderCompletionBlock: { index, errors in
launchRequestsInOrderCompletionBlock(currentIndex, errors)
})
}
else {
// We parse and send all requests
NSLog("Send list finished")
launchRequestsInOrderCompletionBlock(currentIndex, errors)
}
})
}
private func executeRequest (_ requestItemsToSend: [RequestItem], _ index: Int, _ errors: [Error], executeRequestCompletionBlock: @escaping (_ index: Int, _ errors: [Error]) -> Void) {
NSLog("Send request %d", index)
Alamofire.request(requestItemsToSend[index].urlToCall, method: requestItemsToSend[index].method, parameters: requestItemsToSend[index].params, headers: requestItemsToSend[index].headers).responseJSON { response in
var errors: [Error] = errors
switch response.result {
case .success:
// Request sended successfully, we can remove it from not sended request array
self.requestItemsToSend.remove(at: index)
break
case .failure:
// Still not send we append arror
errors.append(response.result.error!)
break
}
NSLog("Receive request %d", index)
executeRequestCompletionBlock(index+1, errors)
}
}
Вызов:
trySendRequestsNotSent()
Результат:
Send list started
Send request 0
Receive request 0
Send request 1
Receive request 1
Send request 2
Receive request 2
...
Send list finished
Подробнее см. Gist
Swift 3: Вы также можете использовать семафоры на этом пути. Это очень полезно, кроме того, вы можете точно отслеживать, когда и какие процессы будут завершены. Это было извлечено из моего кода:
//You have to create your own queue or if you need the Default queue
let persons = persistentContainer.viewContext.persons
print("How many persons on database: \(persons.count())")
let numberOfPersons = persons.count()
for eachPerson in persons{
queuePersonDetail.async {
self.getPersonDetailAndSave(personId: eachPerson.personId){person2, error in
print("Person detail: \(person2?.fullName)")
//When we get the completionHandler we send the signal
semaphorePersonDetailAndSave.signal()
}
}
}
//Here we will wait
for i in 0..<numberOfPersons{
semaphorePersonDetailAndSave.wait()
NSLog("\(i + 1)/\(persons.count()) completed")
}
//And here the flow continues...
Для этой цели вам понадобятся семафоры.
//Create the semaphore with count equal to the number of requests that will be made.
let semaphore = dispatch_semaphore_create(locationsArray.count)
for key in locationsArray {
let ref = Firebase(url: "http://myfirebase.com/" + "\(key.0)")
ref.observeSingleEventOfType(.Value, withBlock: { snapshot in
datesArray["\(key.0)"] = snapshot.value
//For each request completed, signal the semaphore
dispatch_semaphore_signal(semaphore)
})
}
//Wait on the semaphore until all requests are completed
let timeoutLengthInNanoSeconds: Int64 = 10000000000 //Adjust the timeout to suit your case
let timeout = dispatch_time(DISPATCH_TIME_NOW, timeoutLengthInNanoSeconds)
dispatch_semaphore_wait(semaphore, timeout)
//When you reach here all request would have been completed or timeout would have occurred.
Swift 3 или 4
Если вы не заботитесь о [заказах], используйте ответ @ paulvs , он отлично работает.
else на всякий случай, если кто-то хочет получить результат в порядке, а не стрелять одновременно, здесь - это код.
let dispatchGroup = DispatchGroup()
let dispatchQueue = DispatchQueue(label: "taskQueue")
let dispatchSemaphore = DispatchSemaphore(value: 0)
dispatchQueue.async {
// use array categories as an example.
for c in self.categories {
if let id = c.categoryId {
dispatchGroup.enter()
self.downloadProductsByCategory(categoryId: id) { success, data in
if success, let products = data {
self.products.append(products)
}
dispatchSemaphore.signal()
dispatchGroup.leave()
}
dispatchSemaphore.wait()
}
}
}
dispatchGroup.notify(queue: dispatchQueue) {
DispatchQueue.main.async {
self.refreshOrderTable { _ in
self.productCollectionView.reloadData()
}
}
}
Xcode 9.2, Swift 4
class AsyncOperation {
typealias NumberOfPendingActions = Int
typealias DispatchQueueOfReturningValue = DispatchQueue
typealias CompleteClosure = ()->()
private let dispatchQueue: DispatchQueue
private var semaphore: DispatchSemaphore
private var numberOfPendingActionsQueue: DispatchQueue
public private(set) var numberOfPendingActions = 0
var whenCompleteAll: (()->())?
init(numberOfSimultaneousActions: Int, dispatchQueueLabel: String) {
dispatchQueue = DispatchQueue(label: dispatchQueueLabel)
semaphore = DispatchSemaphore(value: numberOfSimultaneousActions)
numberOfPendingActionsQueue = DispatchQueue(label: dispatchQueueLabel + "_numberOfPendingActionsQueue")
}
func run(closure: @escaping (@escaping CompleteClosure)->()) {
self.numberOfPendingActionsQueue.sync {
self.numberOfPendingActions += 1
}
dispatchQueue.async {
self.semaphore.wait()
closure {
self.numberOfPendingActionsQueue.sync {
self.numberOfPendingActions -= 1
if self.numberOfPendingActions == 0 {
self.whenCompleteAll?()
}
}
self.semaphore.signal()
}
}
}
}
let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 1, dispatchQueueLabel: "AnyString")
asyncOperation.whenCompleteAll = {
print("All Done")
}
for i in 0...5 {
print("\(i)")
asyncOperation.run{ completeClosure in
// add any (sync/async) code
//..
// Make signal that this closure finished
completeClosure()
}
}
import UIKit
class ViewController: UIViewController {
let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 3, dispatchQueueLabel: "AnyString")
let button = UIButton(frame: CGRect(x: 50, y: 80, width: 100, height: 40))
let label = UILabel(frame: CGRect(x: 180, y: 50, width: 150, height: 100))
var counter = 1
var labelCounter = 0
override func viewDidLoad() {
super.viewDidLoad()
button.setTitle("Button", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
label.text = "\(labelCounter)"
label.numberOfLines = 2
label.textAlignment = .natural
view.addSubview(button)
view.addSubview(label)
}
@objc func buttonTapped() {
//sample1()
sample2()
}
func sample1() {
print("Sample 1")
labelCounter += 1
label.text = "button tapped \(labelCounter) times"
print("Button tapped at: \(Date())")
asyncOperation.whenCompleteAll = {
print("All Done")
}
asyncOperation.run{ completeClosure in
let counter = self.counter
print(" - Loading action \(counter) strat at \(Date())")
self.counter += 1
DispatchQueue.global(qos: .background).async {
sleep(1)
print(" - Loading action \(counter) end at \(Date())")
completeClosure()
}
}
}
func sample2() {
print("Sample 2")
label.text = ""
asyncOperation.whenCompleteAll = {
print("All Done")
}
for i in 0...5 {
asyncOperation.run{ completeClosure in
let counter = self.counter
print(" - Loading action \(counter) strat at \(Date())")
self.counter += 1
DispatchQueue.global(qos: .background).async {
sleep(UInt32(i+i))
print(" - Loading action \(counter) end at \(Date())")
completeClosure()
}
}
}
}
}
Образец 1
Образец 2