iOS开发多线程下全局变量赋值崩溃原理是什么

发布时间:2022-07-21 09:27:49 作者:iii
来源:亿速云 阅读:156

这篇文章主要介绍了iOS开发多线程下全局变量赋值崩溃原理是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇iOS开发多线程下全局变量赋值崩溃原理是什么文章都会有所收获,下面我们一起来看看吧。

问题 Demo

在多线程下同时给全局变量赋值时会发生崩溃:

static NSObject *_instance;
- (void)foo {
    _instance = [[NSObject alloc] init];
}

崩溃原因

如下为源码的汇编代码:

Demo-iOS`-[ViewController foo]:
    0x104e4e088 <+0>:  stp    x29, x30, [sp, #-0x10]!
    0x104e4e08c <+4>:  mov    x29, sp
    # newValue = [[NSObject alloc] init]
    0x104e4e094 <+12>: ldr    x0, #0x7454               ; (void *)0x00000001db209e08: NSObject
    0x104e4e098 <+16>: bl     0x104e4e438               ; symbol stub for: objc_alloc_init
    # oldValue = _instance
    0x104e4e09c <+20>: adrp   x9, 7
    0x104e4e0a0 <+24>: ldr    x8, [x9, #0x788]
    # _instance = newValue
    0x104e4e0a4 <+28>: str    x0, [x9, #0x788]
    # objc_release(oldValue)
    0x104e4e0a8 <+32>: mov    x0, x8
    0x104e4e0ac <+36>: ldp    x29, x30, [sp], #0x10
    0x104e4e0b0 <+40>: b      0x104e4e480               ; symbol stub for: objc_release

对汇编代码进行反汇编,可以看出 ARC 下编译器添加了读取旧值 oldValue = _instance 和释放旧值 objc_release(oldValue) 的操作:

- (void)foo {
    NSObject *newValue = [[NSObject alloc] init];
    NSObject *oldValue = _instance; //读取旧值
    _instance = newValue; 
    objc_release(oldValue); //释放旧值
}

给全局变量赋值时会读取旧值、释放旧值,旧值是从全局变量读取的,多个线程可以同时读到同一个值,如果一个 线程 在访问旧值时,旧值被其它线程销毁,就会发生崩溃。

即使在代码中添加了判空逻辑,也会有可能多个线程同时进入 if (!_instance) 里,发生错误:

static NSObject *_instance;
- (void)foo {
    if (!_instance) {
        _instance = [[NSObject alloc] init];
    }
}

即使不崩溃,多个线程也会产生不同的实例,是不符合预期的

崩溃路径

可以推断出一种复现崩溃的办法:

验证方式

lldb 的 thread continue 指令可以控制仅一个线程执行,其它线程保持挂起。

利用该指令,可以复现崩溃路径,按下面步骤可以验证:

iOS开发多线程下全局变量赋值崩溃原理是什么

可以多次测试让 3 个 线程 同时进入断点,进入断点后可以看到 Thread 2、3、4 是创建的 3 个线程:

iOS开发多线程下全局变量赋值崩溃原理是什么

不加 asm("nop\n") 的话执行完 objc_release(oldValue) 后,foo 函数会直接结束,不太方便在 objc_release(oldValue) 之后打断点进行调试,添加之后 objc_release 之后会有位置打断点(第 4 5 步用到)

iOS开发多线程下全局变量赋值崩溃原理是什么

iOS开发多线程下全局变量赋值崩溃原理是什么

iOS开发多线程下全局变量赋值崩溃原理是什么

可以看到 Thread 2 创建的实例为 0x0000000280df8020

步骤1:删除断点(每次切换线程都要删掉断点,不然 Xcode 可能会有 bug ...),切换到 Thread 3 ,给第 9 行打断点,并执行 thread continue:

iOS开发多线程下全局变量赋值崩溃原理是什么

在 Xcode Debug Navitor 中选择线程堆栈可以切换线程

或者使用 lldb,thread select 3 切换线程

步骤2:删除断点,切换到 Thread 4,给第 9 行打断点,并执行 thread continue:

iOS开发多线程下全局变量赋值崩溃原理是什么

可以发现 Thread 3、4 读到的旧值都是 Thread 2 创建的 0x0000000280df8020

步骤:删除断点,切换到 Thread 3,给第 12 行打断点,并执行 thread continue:

iOS开发多线程下全局变量赋值崩溃原理是什么

此时 oldValue 引用计数为 0,被销毁

步骤:删除断点,切换到 Thread 4,给第 12 行打断点,并执行 thread continue:

iOS开发多线程下全局变量赋值崩溃原理是什么

在 Thread 3 执行 objc_release(oldValue) 后 oldValue 就已经被销毁了,

Thread 4 再次访问时会发生崩溃

其它测试

 @property (nonatomic, strong) NSObject *obj;
- (NSObject *)getInstance {
    _obj = [[NSObject alloc] init];
    return _obj;
}

iOS开发多线程下全局变量赋值崩溃原理是什么

iOS开发多线程下全局变量赋值崩溃原理是什么

iOS开发多线程下全局变量赋值崩溃原理是什么

局部变量不涉及"将旧值释放"这个操作。

关于“iOS开发多线程下全局变量赋值崩溃原理是什么”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“iOS开发多线程下全局变量赋值崩溃原理是什么”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注亿速云行业资讯频道。

推荐阅读:
  1. linux模拟多线程崩溃和多进程崩溃的代码解析
  2. Python多线程的原理是什么

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

ios

上一篇:C++动态内存管理实例分析

下一篇:JavaScript如何实现像素鸟小游戏

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》