iOS性能優(yōu)化實戰(zhàn)篇-多線程異步加鎖問題

這個問題是我在做人臉識別、活體認(rèn)證的功能時遇到的一個bug。
  • 問題現(xiàn)象
    測試同學(xué)在測試人臉識別時偶爾出現(xiàn)程序Crash,經(jīng)過初步診斷發(fā)現(xiàn)問題時出在第三方SDK,當(dāng)時因為項目進度比較急,就直接把奔潰日記、代碼截圖拋給了第三方對接的同學(xué)。
    結(jié)果經(jīng)第三方同學(xué)說他們測試沒發(fā)現(xiàn)奔潰問題,說后面處理了就告訴我,然后就沒有然后了...

  • 重現(xiàn)問題
    一段時間過后,測試小同學(xué)又來向我反饋這個問題時,我發(fā)誓我一定要解決這個bug。(補充:測試是個男同學(xué)?。?br> 于是我又打開項目調(diào)試了好多次,終于復(fù)現(xiàn)了bug,找到了那段坑爹的代碼,仔細(xì)的研究了一番,終于找到了問題!

  • 坑爹代碼如下

#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>

@interface ViewController ()
//音頻播放器
@property(nonatomic,strong)AVAudioPlayer * audioPlayer;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    //業(yè)務(wù)場景:
    //攝像頭不斷獲取圖像,然后每張圖像進行異步比對,同時不斷將結(jié)果返回代理,然后進行語音操作提示。
    dispatch_queue_t currentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 1; i<4; i++) {
        dispatch_async(currentQueue, ^{
            NSLog(@"播放次數(shù)--- %d ---",i);
            [self playBackgroundSound:i];
        });
    }
}

//播放音頻
- (void) playBackgroundSound:(NSInteger )num {
    
    NSString *soundFilePath = [[NSBundle mainBundle] pathForResource: @"test" ofType: @"mp3"];
    NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: soundFilePath];
    
    @synchronized (_audioPlayer) {
        NSLog(@"%@",[NSThread currentThread]);
        if ([_audioPlayer isPlaying]) {
            [_audioPlayer stop];
        }
        _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL: fileURL error:nil];
        NSLog(@"次數(shù)- %ld ---播放器:%@ ",num,_audioPlayer);
        [_audioPlayer play];
        NSLog(@"次數(shù)- %ld ---播放器:%@ ",num,_audioPlayer);
    }
}

  • 分析問題
    第三方SDK開發(fā)小同學(xué),為了避免并發(fā)隊列異步執(zhí)行時出現(xiàn)資源競爭問題,特地好心地給這個任務(wù)加了個鎖。
@synchronized (_audioPlayer) {

 //這是一個坑爹的初始化,切換音頻內(nèi)容必須進行一個初始化?。?!
    _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL: fileURL error:nil]; 
}

然而問題恰恰就出現(xiàn)在這里,小同學(xué)千不該萬不該把 _audioPlayer對象作為了鎖對象。

  1. 鎖對象_audioPlayer 初始化是在鎖的內(nèi)部,會導(dǎo)致多個任務(wù)進入鎖內(nèi)執(zhí)行;
  2. 初始化是什么:給一個對象分配存儲空間,并返回這個對象(新指針地址)?。?!也就是鎖對象_audioPlayer會一直變!
  3. 當(dāng)任務(wù)1初始化返回一個 A指針賦值給_audioPlayer,正準(zhǔn)備發(fā)送"play"消息時,任務(wù)2也初始化一個B指針賦值給_audioPlayer,原來的A指針沒有變量持有,內(nèi)存即被收回。此時"A指針"變成了一個野指針,此時繼續(xù)向它發(fā)生"play"消息,程序自然會崩潰!
  • 處理方案
  1. 將鎖對象"_audioPlayer"換成"self"。(最簡單、效果最好
  2. 去掉鎖,將異步換成同步執(zhí)行。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容