The Util Designer
SwiftUI 並行性Concurrency的使用 - GCD方式
xcode 13.4.1, swift 5.5, iOS 15.4
2022-09-07
由於現在的電話使用的CPU都是多核的,所以更好的利用多核的優勢,目前很多App都是使用並行性來最大程度的利用多核的計算能力,這次就講講如何使用Swift提供一種並行性方式GCD。
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. 為了解決這種問題,我們可以使用GCD來使用多核的優勢。在Swift中只要把需要很長時間計算或運行的程式放在DispatchQueue.global(qos: .userInitiated).async就可以。
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() {
        DispatchQueue.global(qos: .userInitiated).async {
            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 private var calcState : String = "Heavy Calculation"
    var body: some View {
        VStack {
            HStack {
                Stepper("Number") {
                    num = num + 1
                } onDecrement: {
                    num = num - 1
                }
                Text("\(num)").font(.largeTitle)
            }
            Button {
                heavyCalculation()
            } label: {
                Text(calcState)
            }

        }
    }
    
    func heavyCalculation() {
        calcState = "Calculating ... ..."
        DispatchQueue.global(qos: .userInitiated).async {
            for i in 0...100000 {
                if isPrime(i) {
                    print("\(i) is prime.")
                }
            }
            self.calcState = "Finish"
        }
    }
    
    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
    }
}