Swift线程安全计数器变量?用于跟踪何时删除网络活动指示器

问题描述:

我的场景的快速说明:Swift线程安全计数器变量?用于跟踪何时删除网络活动指示器

我有一个iOS应用程序,可以使用不同的线程同时执行大量的网络调用。在我的iOS应用程序中,我想要在任何网络活动发生时显示内置(在状态栏上)网络活动指示器。

所以我做的是在任何网络调用之前启动活动指示器,然后在网络调用出错或成功完成后停止它。

发生了什么(显然)是一旦一个网络活动行为成功(或错误)完成,那么即使我知道其他网络活动正在发生,它也会删除网络活动指示符。

所以我的解决方案是创建一个静态变量(因为许多类执行网络活动),它跟踪当前正在执行多少个网络请求(每次启动时增加此静态变量,然后递减此静态变量时他们完成(成功或失败),只有当这个计数器达到0,然后关闭网络活动指标

这工作正常,但现在我们得到我的问题 - 许多不同的线程正在更新这个静态变量我的想法是它需要线程安全吗?

什么是最好的方法做到这一点 - 我应该把每个电话开始/停止th e活动指示器(它依次增加/减少静态变量)到主线程上,然后保证这个变量将被串行读取/写入?

或者我一直在阅读有关使用信号量来告诉线程等待,如果变量被锁定,然后运行时,另一个线程在读取和写入变量时完成它?

对不起,我想告诉你我目前的情况。

在此先感谢

实现此目的的最简单方法是串行队列使用情况。 您可以使用主队列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

由于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