Swift全局变量的线程安全分析

一、示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import UIKit
 
let obj = TestObj()
 
class TestObj {
    init() {
        print("\(type(of: self)) init")
    }
}
 
class ViewController: UIViewController {
     
    func test() {
        let x = obj
    }
 
    override func viewDidLoad() {
        super.viewDidLoad()
        print("ViewController start view didload")
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
            for _ in 0...100000 {
                DispatchQueue.global().async {
                    let x = obj
                    print(x)
                }
            }
        }
    }
}

 问题,TestObj 中的初始化方法何时调用?

二、经过测试

  方法在 viewDidLoad 的 log “ViewController start view didload”输出5s之后才会调用

  因为swift中static修饰的全局变量或者类变量都是lazy load,也就是第一次使用的时候才会初始化,并且这个初始化过程是原子化的 

 

 

 

 

三、多线程安全分析 

  结论: 

    对于只读的全局变量,在多线程环境下面读是安全的;

    对于可变的全局变量,多线程下面写是不安全的,可能造成引用计数不正确,错误释放。

    具体可参考:

    https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html

  为什么只读的情况下面多线程访问是安全的,可以看下面的方法

  

 

 

 这个方法的反汇编(DEBUG模式下面,编译器优化之后可能有变化)

1
2
3
4
5
6
7
8
int _$s14GlobalVarTest214ViewControllerC4testyyF() {
    rax = GlobalVarTest2.obj.unsafeMutableAddressor : GlobalVarTest2.TestObj();
    rax = *rax;
    var_18 = rax;
    swift_retain(rax);
    rax = swift_release(var_18);
    return rax;
}

可以看到全局变量在这个作用域内是先进行了retain操作,最后进行了release。

对一个对象的retain和release操作,本身这个方法是原子操作的,所以不用担心这两个方法的线程安全问题。

如果是全局可写的变量,当两个线程对这个对象进行改变的时候

retain和release中间会出现穿插,导致业务上引用计数的变化不再安全。

因此,对于全局变量写操作,必须进行加锁,保证retain和release在正确的逻辑中。

  

posted @   兜兜有糖的博客  阅读(576)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示