2017-11-18 9 views
0

私は1ヶ月以上私を悩ましている難解なバグを修正するのに役立つ必要があります。Swift EXEC_BAD_ACCESSは、AVAudioMixerNodeにインストールされたタップで書き込まれた配列にアクセスするときにスローされます

私は、見るべきコードがたくさんあることは知っていますが、問題の根源と思われるメソッドはinstallReadTapOnPadMixer()メソッドとupdateVolumeData()メソッドです。

私はSwiftにサンプラーアプリを持っています。私のViewControllerの1つでは、ユーザーは "パッド"を押すことができ、ロードされたサウンドが再生されます。

enter image description here

問題は一度に私のアプリがスローながらということである:私は現在、パッドを押したときに、音の現在のボリュームはパッドが着色されているどのように明るく影響するようにそれがセットアップされている

スレッド1:EXC_BAD_ACCESS(コード= 1、アドレス= 0x55555555555550)

または例外は次のようになり、いくつかの例です:

スレッド1:EXC_BAD_ACCESS(コード= 1、アドレス= 0x9c9c3170)

各パッドのモデル側は、AVAudioUnitVarispeedノードの配列に接続されたAVAudioPlayerNodesの配列で構成されています。各varispeedノードは、AVAudioMixerNodeに接続され、AVAudioMixerNodeはAVAudioEngineのメインミキサーノードに接続されます。

だから、再び、それはです:

AVAudioPlayerNode [] - > AVAudioUnitVarispeed [] - > AVAudioMixerNode - >メインミキサーノード。

モデル側の任意のパッドの現在の音量を得るには、チェーンの各ミキサーノードにタップがインストールされている必要があります。これと同じように:私は私の「MasterSoundMod」クラスのプレーを()を呼び出すとき

/** UIの強化、 プレイヤノードの現在の再生音量を取得します*/

private func installReadTapOnPadMixer(bankNumber: Int, padNumber: Int) 
{ 
    guard(_soundCollection[bankNumber]![padNumber]?.tapInstalled == false) else{ return; } 

    // https://miguelsaldana.me/2017/03/18/how-to-create-a-volume-meter-in-swift-3/ 
    _padMixerArray[bankNumber]![padNumber]?.installTap(onBus: 0, bufferSize: 1024, format: _outputFormat) 
    { 
     (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) in 
      let dataptrptr = buffer.floatChannelData!; 
      let dataptr = dataptrptr.pointee; 
      let datum = dataptr[Int(buffer.frameLength) - 1]; 
      self._padMixerVolumeDataArray[bankNumber][padNumber] = fabs(datum); 
    } 

    _soundCollection[bankNumber]![padNumber]?.tapInstalled = true; 
} 

はその後、私はタイマーをスタート.scに渡された私のセレクタ機能に続いて

/**パッドの現在のサウンド設定を再生する*/

func play(bankNumber: Int, padNumber: Int, preview: Bool) 
{ 
    if(!_isConnected && !_routeChanging) 
    { 
     if((_soundCollection[bankNumber]![padNumber]) != nil && (_soundCollection[bankNumber]![padNumber])!.isLoaded) 
     {  

      if(!(_soundCollection[bankNumber]![padNumber]?.playerNodeArray[(_soundCollection[bankNumber]![padNumber]?.currentPlayIndex)!]!.engine?.isRunning)!) 
      { 
       startMod(); // start the AVAudioEngine and update a few state variables 
      } 

      // [[PadModel]] 
      (_soundCollection[bankNumber]![padNumber])!.play(); 
     } 
    } 

    // the timer must not be scheduled for any preview or any blank pad 
    if(!preview) 
    { 
     _volumeDataTimerArray[bankNumber][padNumber] = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(updateVolumeData(timer:)), userInfo: (bankNumber, padNumber), repeats: true); 
    } 
} 

:そうのような蛇口からデータを読み出すために、 heduledTimer()私が持っている:

/**ここから現在に触れパッドの現在の音量を通過し、所有している歌、 まで、その後、パッドのモデルに対応するPadViewオブジェクトへのラインダウン が現在再生されています*/

@objc func updateVolumeData(timer: Timer) 
{ 
    let userInfo = timer.userInfo as! (Int, Int); 

    _delegate!.passCurrentVolumeToPadView(bankNumber: userInfo.0, padNumber: userInfo.1, volume: _padMixerVolumeDataArray[userInfo.0][userInfo.1]); // <- EXEC_BAD_ACCESS 

例外は_delegate!の呼び出しでスローされます。passCurrentVolumeToPadView() はまた、私はそうのようにstop()メソッドで私のタイマーを無効にする:

/**)(銀行とパッド番号*/

func stop(bankNumber: Int, padNumber: Int) 
{ 
    _releaseTimerArray[bankNumber][padNumber] = Timer() 

    var preFadeVolume: Float = 0.0; 

    if(_padMixerArray[bankNumber]![padNumber] != nil){ preFadeVolume = (_padMixerArray[bankNumber]![padNumber]?.volume)!; } 

    // no player node manipulation while audio route change is in progress. 
    if(!_isConnected && !_routeChanging) 
    { 
     if((_soundCollection[bankNumber]![padNumber]) != nil && (_soundCollection[bankNumber]![padNumber])!.isLoaded) 
     { 
      _releaseTimerArray[bankNumber][padNumber] = Timer.scheduledTimer(timeInterval: 0.001, target: self, selector: #selector(self.fadeout(timer:)), userInfo: (bankNumber, padNumber, preFadeVolume), repeats: true); 
     } 

     _volumeDataTimerArray[bankNumber][padNumber]?.invalidate(); 
    } 

    // for good measure..... 
    //  this DOES need to be here! 
    if(_volumeDataTimerArray[bankNumber][padNumber] != nil && (_volumeDataTimerArray[bankNumber][padNumber]!.isValid)) 
    { 
     _volumeDataTimerArray[bankNumber][padNumber]?.invalidate(); 
    } 
} 

停止に対応するパッドの再生を停止メソッドは、厄介なクリック音を避けるためにフェードアウトされるサウンドを実行するタイマーをスケジュールしますが、それはこの質問の一部ではありません。

私はこの問題を解決するためにいくつかのことを試みました。もともとは、単一のタイマーと1つの_volumeData変数を使用していました。これらのメンバーの両方を配列に昇格させると、例外がスローされる頻度が大幅に減少します。

私はアプリを起動するときに、パッドを5分間押してから例外がスローされることはありません。私はアプリを起動すると、他の回、例外はパッドを押す1分以内にスローされます。

もちろん、私のinstallReadTapOnPadMixer()のinstallTap()の呼び出しでのコードブロックは、私のメインスレッドの外で実行されます。

例外を引き起こしていることについての私の素朴な仮説は、私の_padMixerVolumeDataArray []が読み込まれるのと同時に書き込まれるということです。繰り返しますが、これは素朴です。

もう少し問題を調査した後、EXEC_BAD_ACCESSに関するコンセンサスは、リリースされたオブジェクトにアクセスしようとするとスローされるように見えます。だから、もっと正確な説明は、対応するタイマーが無効にされた後にupdateVolumeData()メソッドのコードが呼び出されるということです。

また、私は見るべきコードが多いと知っていますが、問題の根源に思われるメソッドはinstallReadTapOnPadMixer()メソッドとupdateVolumeData()メソッドです。

どのような洞察力であれ、どんな身体にも非常に感謝しています!

+0

を例外ブレークポイントを追加し、プロジェクトを実行し、それがうクラッシュを起こす原因となる行を止めてください。それはあなたが問題を切り離すのに役立ちます – user1046037

答えて

0

まあ、私は新人ミスをしました。私の解決策を見つける前に、問題の配列は次のように宣言されました:

プライベート遅延var _padMixerVolumeDataArray:[[Float?]?]!]! = [];

この悪い宣言は、私のアプリでさまざまなメモリリークの問題を解決しようとしたことに対応していました。

exc_bad_accessがスローされずに宣言を変更してパッドを55分間マッシュした後、問題が解決されたとの結論に達しました。新しい宣言:

プライベートvar _padMixerVolumeDataArray:[[Float]] = [];

私はこのことから怠惰なキーワードを削除するためにアイデアを得た:http://blog.swilliams.me/words/2015/03/31/tracking-down-an-exc-bad-access-with-swift/

問題が解決されるまで、私は見つけられませんでした別の同様の質問: Why does my @lazy property crash, but if I make it non lazy it works?

関連する問題