返回
dispatch_once 的隐形陷阱与注意事项
IOS
2023-12-27 20:00:54
单例模式在 Objective-C 开发中十分常见,而利用 dispatch_once
实现单例更是简便易行。然而,近期在为维护中的老项目增添新功能时,我却无意中掉入了 dispatch_once
的陷阱。
具体来说,我们的问题如下:公司测试表明,在安装应用一段时间后,重新启动应用会导致不可预期的崩溃。
经过排查,我们发现问题出在利用 dispatch_once
创建单例的代码上。在 init
方法中,我们使用了如下代码:
dispatch_once(&token, ^{
// 初始化单例实例
});
这会导致在应用启动时创建单例,并将 token
标记为已执行。然而,当应用从后台重新启动时,dispatch_once
的 token
仍然标记为已执行,但此时单例实例已经被释放。
结果就是,在重新启动时,代码会尝试再次初始化单例,这会导致崩溃。为了解决这个问题,我们必须考虑应用重新启动的情况,并在 init
方法中添加以下代码:
if (dispatch_once(&token)) {
// 单例实例已存在
return;
}
这样,在应用重新启动时,当 dispatch_once
的 token
仍然标记为已执行时,代码会直接返回,避免再次创建单例。
除了这个陷阱,在使用 dispatch_once
时还有一些其他注意事项:
- 避免在多线程环境中使用:
dispatch_once
并不是线程安全的,因此在多线程环境中使用它可能会导致数据竞争和崩溃。 - 正确处理
dispatch_once
宏的参数:dispatch_once
宏接受两个参数:dispatch_once_t
类型的token
和一个包含初始化代码的块。确保token
是唯一的,并且初始化块只执行一次。 - 考虑单例的销毁:
dispatch_once
创建的单例在整个应用生命周期中都是存在的。如果单例持有大量资源或状态,则需要在应用退出时释放这些资源。
总而言之,dispatch_once
是实现单例模式的便捷方法,但需要谨慎使用以避免潜在的陷阱。通过了解其限制并采取适当的预防措施,我们可以确保 dispatch_once
为我们的 Objective-C 代码提供稳定可靠的单例实现。