2017-07-01 20 views
1

我的場景的快速說明:Swift線程安全計數器變量?用於跟蹤何時刪除網絡活動指示器

我有一個iOS應用程序,可以使用不同的線程同時執行大量的網絡調用。在我的iOS應用程序中,我想要在任何網絡活動發生時顯示內置(在狀態欄上)網絡活動指示器。

所以我做的是在任何網絡調用之前啓動活動指示器,然後在網絡調用出錯或成功完成後停止它。

發生了什麼(顯然)是一旦一個網絡活動行爲成功(或錯誤)完成,那麼即使我知道其他網絡活動正在發生,它也會刪除網絡活動指示符。

所以我的解決方案是創建一個靜態變量(因爲許多類執行網絡活動),它跟蹤當前正在執行多少個網絡請求(每次啓動時增加此靜態變量,然後遞減此靜態變量時他們完成(成功或失敗),只有當這個計數器達到0,然後關閉網絡活動指標

這工作正常,但現在我們得到我的問題 - 許多不同的線程正在更新這個靜態變量我的想法是它需要線程安全嗎?

什麼是最好的方法做到這一點 - 我應該把每個電話開始/停止th e活動指示器(它依次增加/減少靜態變量)到主線程上,然後保證這個變量將被串行讀取/寫入?

或者我一直在閱讀有關使用信號量來告訴線程等待,如果變量被鎖定,然後運行時,另一個線程在讀取和寫入變量時完成它?

對不起,我想告訴你我目前的情況。

在此先感謝

回答

1

實現此目的的最簡單方法是串行隊列使用情況。 您可以使用主隊列DispatchQueue.main.async { /* increment or decrement counter */ }DispatchQueue.main.sync { /* increment or decrement counter */ }

也可以創建新的串行隊列:let counterQueue = DispatchQueue(label: "counter-queue"),並用它爲同樣的目的。

+0

如果使用主隊列它應該是同步或異步?異步只是說它可以在任何時候運行?或者說它可以在功能中途暫停以允許其他運行?好像它可以暫停一下,那麼我想我沒有更好? – Michael

+0

同步將停止執行當前的runloop,並等待直到您進入隊列的塊完成執行。 Async在當前作用域中的代碼完成執行後運行代碼。只是不要從主線程調用'DispatchQueue.main.sync',因爲它會導致死鎖。 –

+0

啊,我明白了,歡呼。想想我將創建一個新的隊列來串行化這些獲取和設置。感謝您的幫助 – Michael

2

由於Sviatoslav Yakymiv said,串行調度隊列 可用於同步訪問計數器,該計數器保持當前數量的未完成的網絡請求。

爲此目的可以使用DispatchQueue.main,因爲UIApplication.shared.isNetworkActivityIndicatorVisible只能是在主隊列上訪問的 。

最重要的是,計數器和網絡指示符 的管理可以封裝成一類在一個 RAII樣方式:

final public class NetworkActivity { 

    private static var count = 0 

    public init() { 
     DispatchQueue.main.async { 
      if NetworkActivity.count == 0 { 
       UIApplication.shared.isNetworkActivityIndicatorVisible = true 
      } 
      NetworkActivity.count += 1 
     } 
    } 

    deinit { 
     DispatchQueue.main.async { 
      NetworkActivity.count -= 1 
      if NetworkActivity.count == 0 { 
       UIApplication.shared.isNetworkActivityIndicatorVisible = false 
      } 
     } 
    } 
} 

創建NetworkActivity實例增加了全局計數器, 第一種情況使網絡指示符可見。 銷燬對象會減少計數器,並且在最後一個實例被銷燬時隱藏指示器 。

實例:

let request = URLRequest(...) 
var activity: NetworkActivity? = NetworkActivity() 
_ = activity // To suppress a "variable was written to, but never read" warning 
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in 
    activity = nil 
    // ... 

} 
task.resume() 
+0

也是一個不錯的解決方案! – Michael