I have an escaping function, which completes once a condition is met:
private func xxxfastLoadLSecurityDescriptions(session: URLSession, mySymbols: [String]?, completion: @escaping(Bool) ->()) {
var counter = mySymbols?.count ?? 0
if counter == 0 { completion(false) }
var doubleCount = 0
// print("DESCRIPTION Starting Counter = \(counter)")
for symbolIndex in 0..<(mySymbols?.count ?? 0) {
guard let mySymbol = mySymbols?[symbolIndex] else { print("ERROR in fastLoadLSecurityDescriptions loop: No Symbol") ; continue }
guard let myGetDescriptionRequest = GenericDataRequest(dataToSend: [mySymbol], token: sessionToken, mgbRoute: MGBServerRoutes.retrieveSecurityDescriptionsRoute!)
else { print("Error Getting Security Description Request for \(mySymbol)") ; return }
mySessionSendMGBGenericRequest(session: session, request: myGetDescriptionRequest) { [weak self] success, serverMessage, returnUNISecurityDescription in
guard let self = self else { print("ERROR: self is nil") ; return }
if returnUNISecurityDescription?.count == 0 { print("nil returnUniSecurityDescription for \(mySymbol)") }
// print("DESCRIPTIONS COUNTER = \(counter)")
counter -= 1
var myDescription = UNISecurityDescription()
if returnUNISecurityDescription != nil, returnUNISecurityDescription?.count != 0 { myDescription = returnUNISecurityDescription![0] }
if myDescription.name == nil || myDescription.name == "" { print("Error: No Name for \(String(describing: mySymbol))") }
let myContainersIndices = self.myUNIList.singleContainer.indices.filter({ self.myUNIList.singleContainer[$0].ticker?.symbol == mySymbol })
var myPathArray = [IndexPath]()
for index in 0..<myContainersIndices.count {
self.myUNIList.singleContainer[myContainersIndices[index]].name = myDescription.name
self.myUNIList.singleContainer[myContainersIndices[index]].currencySymbol = myDescription.currencySymbol
self.myUNIList.singleContainer[myContainersIndices[index]].fillFundamentals() // --> Fills the outputs for sortdata
myPathArray.append(IndexPath(row: myContainersIndices[index], section: 0))
}
DispatchQueue.main.async {
self.filteredData = self.myUNIList.singleContainer
self.myCollection?.reloadItems(at: myPathArray)
}
if counter == 0 { // THIS IS TRUE MORE THAN ONCE WHILE IT SHOULD NOT BE TRU MORE THAN ONCE
if doubleCount > 0 { print("WHY!!!!") }
doubleCount += 1
print("DESCRIPTIONS counter = \(counter) -> \(self.myUNIList.listName) - symbols: \(String(describing: mySymbols?.count)) \n==================================================\n")
DispatchQueue.main.async { self.sortNormalTap("Load") { _ in self.displayAfterLoading() } }
completion(true)
return
}
}
}
}
The condition to be met is counter == 0. Once this is met, the function completes and exits a DispatchGroup. The problem is that counter == 0 is true multiple times (with obvious crash in exiting the DispatchGroup). I really cannot understand why this condition is met more than once. The code is pretty linear and I cannot see what causes this. Any help is very appreciated. This is running me crazy.
You're code isn't thread safe, the counter in particular. I wrote an example using your same logic to show illustrate this. If you run it a few times, you will eventually hit the same condition in which your issue is happening.
override func viewDidLoad() {
super.viewDidLoad()
let mySymbols: [Int] = Array(0...100)
for _ in 0..<100 {
xxxfastLoadLSecurityDescriptions(session: URLSession.shared, mySymbols: mySymbols) { (success, counter, doubleCount) in
print("Completed: \(success), Counter: \(counter), Double Count: \(doubleCount)")
}
}
}
private func xxxfastLoadLSecurityDescriptions(session: URLSession, mySymbols: [Int]?, completion: @escaping(Bool, Int, Int) ->()) {
var counter = mySymbols?.count ?? 0
if counter == 0 {
return completion(false, -1, -1)
}
var doubleCount = 0
for symbolIndex in 0..<(mySymbols?.count ?? 0) {
guard let _ = mySymbols?[symbolIndex] else {
print("Error")
continue
}
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(Int.random(in: 50..<900))) {
counter -= 1
DispatchQueue.main.async {
self.view.layoutIfNeeded()
}
if counter == 0 {
if doubleCount > 0 {
// This will eventually print even though logically it shouldn't
print("*****Counter: \(counter), Double Count: \(doubleCount), Symbol Index: \(symbolIndex)")
}
doubleCount += 1
completion(true, counter, doubleCount)
return
}
}
}
}
Output:
Completed: true, Counter: 0, Double Count: 1
Completed: true, Counter: 0, Double Count: 1
Completed: true, Counter: 0, Double Count: 1
Completed: true, Counter: 0, Double Count: 1
Completed: true, Counter: 0, Double Count: 1
Completed: true, Counter: 0, Double Count: 1
Completed: true, Counter: 0, Double Count: 1
Completed: true, Counter: 0, Double Count: 1
*******************************************************************
***** Counter: 0, Double Count: 1, Symbol Index: 15
*******************************************************************
Completed: true, Counter: 0, Double Count: 2
Completed: true, Counter: 0, Double Count: 1
Completed: true, Counter: 0, Double Count: 1
Completed: true, Counter: 0, Double Count: 1
*******************************************************************
***** Counter: 0, Double Count: 1, Symbol Index: 26
*******************************************************************
Completed: true, Counter: 0, Double Count: 2
Completed: true, Counter: 0, Double Count: 1
Completed: true, Counter: 0, Double Count: 1
Completed: true, Counter: 0, Double Count: 1
Completed: true, Counter: 0, Double Count: 1
*******************************************************************
***** Counter: 0, Double Count: 1, Symbol Index: 57
*******************************************************************
Completed: true, Counter: 0, Double Count: 2
*******************************************************************
***** Counter: 0, Double Count: 1, Symbol Index: 3
*******************************************************************
Completed: true, Counter: 0, Double Count: 2
At the end I solved this by using DispatchGroup as follows (simplified code):
private func xxxFastLoadLSecurityDescriptions(session: URLSession, mySymbols: [String]?, completion: @escaping(Bool) ->()) {
let myGroup = DispatchGroup()
for symbolIndex in 0..<(mySymbols?.count ?? 0) {
myGroup.enter()
mySessionSendMGBGenericRequest(...) { [weak self] returnValue in
guard let self = self else { myGroup.leave() ; return }
// do some stuff with returnValue
myGroup.leave()
}
}
myGroup.notify(queue: .main, execute: {
completion(true)
})
}
My question is: is it possible that I run into the same problem as before? For example, say I have a loop for 2 items. The firs item enters the group and say the before the second item enters the loop the async call returns a value and the first item exits the group. At this point, before the second item enters the group the .notify should be triggered and completion(true) exists the function before the second item is processed. Is this possible?
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.