简体   繁体   中英

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. 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM