溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

iOS開發(fā)多線程下全局變量賦值崩潰原理是什么

發(fā)布時間:2022-07-21 09:27:49 來源:億速云 閱讀:156 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹了iOS開發(fā)多線程下全局變量賦值崩潰原理是什么的相關(guān)知識,內(nèi)容詳細(xì)易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇iOS開發(fā)多線程下全局變量賦值崩潰原理是什么文章都會有所收獲,下面我們一起來看看吧。

問題 Demo

在多線程下同時給全局變量賦值時會發(fā)生崩潰:

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

對匯編代碼進(jìn)行反匯編,可以看出 ARC 下編譯器添加了讀取舊值 oldValue = _instance 和釋放舊值 objc_release(oldValue) 的操作:

- (void)foo {
    NSObject *newValue = [[NSObject alloc] init];
    NSObject *oldValue = _instance; //讀取舊值
    _instance = newValue; 
    objc_release(oldValue); //釋放舊值
}

給全局變量賦值時會讀取舊值、釋放舊值,舊值是從全局變量讀取的,多個線程可以同時讀到同一個值,如果一個 線程 在訪問舊值時,舊值被其它線程銷毀,就會發(fā)生崩潰。

即使在代碼中添加了判空邏輯,也會有可能多個線程同時進(jìn)入 if (!_instance) 里,發(fā)生錯誤:

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

即使不崩潰,多個線程也會產(chǎn)生不同的實例,是不符合預(yù)期的

崩潰路徑

可以推斷出一種復(fù)現(xiàn)崩潰的辦法:

  • A B C 線程同時進(jìn)入 - (NSObject *)foo 方法

  • A 線程先創(chuàng)建 NSObject 實例,賦值給 _instance (_instance = newValue),_instance 引用計數(shù)為 1

  • B、C 線程再開始執(zhí)行,執(zhí)行到 oldValue = _instance 時,會從 _instance 全局變量中讀到 A 線程創(chuàng)建的對象,賦值給各自的 oldValue,oldValue 引用計數(shù)為 1

  • B 線程在 objc_release(oldValue) 后會釋放 oldValue,oldValue 引用計數(shù)為 0,oldValue 被銷毀

  • C 線程在 objc_release(oldValue) 時訪問 oldValue,發(fā)生崩潰

驗證方式

lldb 的 thread continue 指令可以控制僅一個線程執(zhí)行,其它線程保持掛起。

利用該指令,可以復(fù)現(xiàn)崩潰路徑,按下面步驟可以驗證:

  • 準(zhǔn)備三個線程執(zhí)行 [self foo],并在 -foo 方法里面打上斷點:

iOS開發(fā)多線程下全局變量賦值崩潰原理是什么

可以多次測試讓 3 個 線程 同時進(jìn)入斷點,進(jìn)入斷點后可以看到 Thread 2、3、4 是創(chuàng)建的 3 個線程:

iOS開發(fā)多線程下全局變量賦值崩潰原理是什么

不加 asm("nop\n") 的話執(zhí)行完 objc_release(oldValue) 后,foo 函數(shù)會直接結(jié)束,不太方便在 objc_release(oldValue) 之后打斷點進(jìn)行調(diào)試,添加之后 objc_release 之后會有位置打斷點(第 4 5 步用到)

iOS開發(fā)多線程下全局變量賦值崩潰原理是什么

iOS開發(fā)多線程下全局變量賦值崩潰原理是什么

  • 在 Thread 2 中給匯編代碼第 10 行打斷點,并執(zhí)行 thread contine,使 Thread 2 執(zhí)行完 _instance = newValue :

iOS開發(fā)多線程下全局變量賦值崩潰原理是什么

可以看到 Thread 2 創(chuàng)建的實例為 0x0000000280df8020

  • 使 Thread 3、4 線程 執(zhí)行完 oldValue = _instance

步驟1:刪除斷點(每次切換線程都要刪掉斷點,不然 Xcode 可能會有 bug ...),切換到 Thread 3 ,給第 9 行打斷點,并執(zhí)行 thread continue:

iOS開發(fā)多線程下全局變量賦值崩潰原理是什么

在 Xcode Debug Navitor 中選擇線程堆??梢郧袚Q線程

或者使用 lldb,thread select 3 切換線程

步驟2:刪除斷點,切換到 Thread 4,給第 9 行打斷點,并執(zhí)行 thread continue:

iOS開發(fā)多線程下全局變量賦值崩潰原理是什么

可以發(fā)現(xiàn) Thread 3、4 讀到的舊值都是 Thread 2 創(chuàng)建的 0x0000000280df8020

  • 使Thread 3 執(zhí)行完 objc_release(oldValue)

步驟:刪除斷點,切換到 Thread 3,給第 12 行打斷點,并執(zhí)行 thread continue:

iOS開發(fā)多線程下全局變量賦值崩潰原理是什么

此時 oldValue 引用計數(shù)為 0,被銷毀

  • 使 Thread 4 執(zhí)行 objc_release(oldValue), 訪問 oldValue

步驟:刪除斷點,切換到 Thread 4,給第 12 行打斷點,并執(zhí)行 thread continue:

iOS開發(fā)多線程下全局變量賦值崩潰原理是什么

在 Thread 3 執(zhí)行 objc_release(oldValue) 后 oldValue 就已經(jīng)被銷毀了,

Thread 4 再次訪問時會發(fā)生崩潰

其它測試

  • 對成員變量賦值時同樣有這個問題

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

iOS開發(fā)多線程下全局變量賦值崩潰原理是什么

  • 局部變量不會有這個問題

iOS開發(fā)多線程下全局變量賦值崩潰原理是什么

iOS開發(fā)多線程下全局變量賦值崩潰原理是什么

局部變量不涉及"將舊值釋放"這個操作。

關(guān)于“iOS開發(fā)多線程下全局變量賦值崩潰原理是什么”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“iOS開發(fā)多線程下全局變量賦值崩潰原理是什么”知識都有一定的了解,大家如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

ios
AI