[英]Swift Code Running Twice When It Should Not
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.要满足的条件是 counter == 0。一旦满足,该函数完成并退出 DispatchGroup。 The problem is that counter == 0 is true multiple times (with obvious crash in exiting the DispatchGroup).问题是 counter == 0 多次为真(退出 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):最后我通过使用 DispatchGroup 解决了这个问题,如下(简化代码):
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.例如,假设我有 2 个项目的循环。 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.此时,在第二项进入组之前,应该触发 .notify 并且在处理第二项之前完成(true) 存在函数。 Is this possible?这可能吗?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.