Swift中使用Protocol实现Loader如何避免重复显示且等全部请求结束再隐藏
现有方案可行性分析
你用计数控制Loader显隐的核心思路是完全可以满足需求的,但你当前的代码和「静态count」的选型存在明显问题:
- 首先你的代码无法直接编译:Swift 协议扩展不支持直接添加存储属性,你代码里用到的
count没有定义,运行会报错。 - 如果使用静态count变量会出现跨实例干扰:静态变量属于类全局共享,如果你同时有多个
UIViewController实例显示Loader,计数会全部算在一起,导致某个页面的Loader提前隐藏或者一直不消失。 - 存在线程安全隐患:如果
showLoader/hideLoader在不同线程调用,count的加减操作不是原子性的,容易出现计数错误,最终导致Loader状态异常。
计数方案的正确实现
如果要保留计数逻辑,不要用静态变量,改用关联对象为每个UIViewController实例绑定独立的计数属性即可,参考实现代码:
import ObjectiveC.runtime protocol Loader where Self: UIViewController { func showLoader() func hideLoader() } // 关联对象的key private var loaderCountKey: UInt8 = 0 private let loaderQueue = DispatchQueue(label: "com.example.loader.queue") // 保证计数操作线程安全 extension Loader { private var count: Int { get { loaderQueue.sync { objc_getAssociatedObject(self, &loaderCountKey) as? Int ?? 0 } } set { loaderQueue.async { objc_setAssociatedObject(self, &loaderCountKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } } func showLoader() { let currentCount = count if currentCount == 0 { print("Show Loading") // 这里添加你的Loader视图到当前控制器的逻辑 } count = currentCount + 1 } func hideLoader() { let currentCount = count count = max(0, currentCount - 1) if count == 0 { print("Hide Loading") // 这里添加你的Loader移除逻辑 } } }
额外建议:可以在UIViewController的viewDidDisappear方法里加兜底逻辑,页面消失时直接把count置0并隐藏Loader,避免请求未返回页面销毁导致的Loader泄漏。
不用count的替代方案
如果不想自己维护计数,可以选以下更省心的实现:
- Token引用计数方案:每次调用
showLoader返回一个销毁令牌,当所有令牌都被释放(对应所有请求结束)时自动隐藏Loader。可以借助Combine的AnyCancellable实现,也可以自己封装简单的Token类,在deinit时自动执行隐藏逻辑。 - 绑定请求管理器方案:如果你的项目有统一的API请求管理类,可以直接在管理器里维护当前页面的活跃请求数,请求发起时自动触发Loader显示,所有请求结束自动触发隐藏,不需要业务层手动调用
show/hide方法。 - 标识集合方案:用
Set代替数字计数,每次发起请求生成唯一ID,show时把ID加入集合,hide时移除ID,集合为空时才隐藏Loader。这种方案可以避免同一个请求重复调用show导致的计数冗余,适合请求可能重试的场景。
内容的提问来源于stack exchange,提问作者Antony
相关产品推荐
相关产品推荐

