The Util Designer
SwiftUI 並行性Concurrency的使用 - OperationQueue方式
xcode 13.4.1, swift 5.5, iOS 15.4
2022-09-07
由於現在的電話使用的CPU都是多核的,所以更好的利用多核的優勢,目前很多App都是使用並行性來最大程度的利用多核的計算能力,這次就講講如何使用Swift提供一種並行性方式OperationQueue。
1. 下面我演示一個要做很長時間的動作,會影響其他元作,如下按下Stepper是沒有反應,要等到做完計算後才會有反應:
import SwiftUI

struct ConcurrencyExample: View {
    @State private var num : Int = 0
        
    var body: some View {
        VStack {
            HStack {
                Stepper("Number") {
                    num = num + 1
                } onDecrement: {
                    num = num - 1
                }
                Text("\(num)").font(.largeTitle)
            }
            Button {
                heavyCalculation()
            } label: {
                Text("Heavy Calculation")
            }

        }
    }
    
    func heavyCalculation() {
        for i in 0...100000 {
            if isPrime(i) {
                print("\(i) is prime.")
            }
        }
    }
    
    func isPrime(_ n : Int ) -> Bool {
        if n<=1 {
            return false
        }
        if n<=3 {
            return true
        }
        var i = 2
        var flag = false
        
        while (!flag && i<n) {
            flag = (n % i == 0)
            i = i + 1
        }
        return !flag
    }
}
2. 為了解決這種問題,我們可以使用OperationQueue來使用多核的優勢。我們創建一個繼承Operation的class,並在func main()寫上需要很長時間計算或運行的程式,然後把這種class的object加入OperationQueue中就可以使用多核的優勢了。
import SwiftUI

struct ConcurrencyExample: View {
    @State private var num : Int = 0
        
    var body: some View {
        VStack {
            HStack {
                Stepper("Number") {
                    num = num + 1
                } onDecrement: {
                    num = num - 1
                }
                Text("\(num)").font(.largeTitle)
            }
            Button {
                heavyCalculation()
            } label: {
                Text("Heavy Calculation")
            }

        }
    }
    
    func heavyCalculation() {
        let queue = OperationQueue()
        let heavyCalculationOperation = HeavyCalculationOperation()
        queue.addOperation(heavyCalculationOperation)
        
    }
    

}

class HeavyCalculationOperation : Operation {
    override func main() {
        heavyCalculation()
    }
    
    func heavyCalculation() {
        for i in 0...100000 {
            if isPrime(i) {
                print("\(i) is prime.")
            }
        }
    }
    
    func isPrime(_ n : Int ) -> Bool {
        if n<=1 {
            return false
        }
        if n<=3 {
            return true
        }
        var i = 2
        var flag = false
        
        while (!flag && i<n) {
            flag = (n % i == 0)
            i = i + 1
        }
        return !flag
    }
}
3. 以上不太明顯,我們可以在計算過程之中改變Button的顯示文字,來更明顯的展示計算的過程,並在計 算過程中,其他元件不受影響。
import SwiftUI

struct ConcurrencyExample: View {
    @State private var num : Int = 0
    @State var calculateState : String = "Calculation"
    var body: some View {
        VStack {
            HStack {
                Stepper("Number") {
                    num = num + 1
                } onDecrement: {
                    num = num - 1
                }
                Text("\(num)").font(.largeTitle)
            }
            Button {
                heavyCalculation()
            } label: {
                Text(calculateState)
            }

        }
    }
    
    func setCalculateState(_ calculateState : String) {
        self.calculateState = calculateState
    }
    
    func heavyCalculation() {
        let queue = OperationQueue()
        let heavyCalculationOperation = HeavyCalculationOperation(concurrencyExample: self)
        queue.addOperation(heavyCalculationOperation)
        
    }
    

}

class HeavyCalculationOperation : Operation {
    
    let concurrencyExample : ConcurrencyExample
    
    init(concurrencyExample : ConcurrencyExample) {
        self.concurrencyExample = concurrencyExample
    }
    
    override func main() {
        self.concurrencyExample.setCalculateState("Calculating ... ...")
        heavyCalculation()
        self.concurrencyExample.setCalculateState("Finish")

    }
    
    func heavyCalculation() {
        for i in 0...100000 {
            if isPrime(i) {
                print("\(i) is prime.")
                self.concurrencyExample.setCalculateState("\(i) is prime.")

            }
        }
    }
    
    func isPrime(_ n : Int ) -> Bool {
        if n<=1 {
            return false
        }
        if n<=3 {
            return true
        }
        var i = 2
        var flag = false
        
        while (!flag && i<n) {
            flag = (n % i == 0)
            i = i + 1
        }
        return !flag
    }
}