나는 한 달 동안 나를 괴롭히는 애매한 버그를 수정하는 데 도움이 필요합니다.스위프트 EXEC_BAD_ACCESS는 AVAudioMixerNode에 설치된 탭으로 작성된 배열에 액세스 할 때 throw됩니다.
내가보기에 코드가 많지만 문제의 근원 인 것으로 보이는 메소드는 installReadTapOnPadMixer() 및 updateVolumeData() 메소드입니다.
저는 스위프트에 샘플러 앱이 있습니다. 내 ViewController 중 하나에서 사용자는 "패드"를 누를 수 있으며로드 된 사운드가 재생됩니다.
문제는 한 번에 내 애플 던져 동안이다 : 저는 현재 패드를 누를 때 소리의 현재 볼륨 패드가 착색하는 방법을 밝게 영향을 미치지 않도록이 설정이
스레드 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() 또한, 난과 같이 정지() 메소드 내 타이머를 무효화 :
/**) (은행 및 패드 번호 */
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();
}
}
정지에 해당하는 패드 재생을 중지 메서드는 불쾌한 클릭 소리를 피하기 위해 페이드 아웃되는 사운드를 실행하도록 타이머를 예약하지만 이는이 질문의 일부가 아닙니다.
나는이 문제를 해결하기 위해 몇 가지 방법을 시도했다. 나는 원래 단일 타이머와 단일 _volumeData 변수를 사용하고있었습니다. 이러한 멤버를 모두 배열로 승격하면 예외가 throw되는 빈도가 크게 줄어 듭니다.
때때로 앱을 실행하면 5 분 동안 패드를 누르기 만하면 예외가 표시되지 않습니다. 다른 경우 앱을 실행하면 잠깐 기다려야 만 예외가 발생합니다.
이제 installTapOnPadMixer()의 installTap() 호출에서 코드 블록이 내 메인 스레드 외부에서 실행됩니다.
예외를 일으키는 것에 대한 나의 순진 가설은 내 _padMixerVolumeDataArray []가 읽는 중 동시에 쓰여지고 있다는 것입니다. 다시 말하지만, 이것은 순진합니다.
문제를 조금 더 살펴본 후에 EXEC_BAD_ACCESS에 대한 합의는 출시 된 개체에 액세스하려고 할 때 발생하는 것으로 보입니다. 그래서 아마도 더 정확한 설명은 해당 타이머가 무효화 된 후에 updateVolumeData() 메서드의 코드가 호출된다는 것입니다.
다시 살펴보아야 할 코드가 많지만 문제의 근원 인 것으로 보이는 메소드는 installReadTapOnPadMixer() 및 updateVolumeData() 메소드입니다.
통찰력이있는 몸에 많은 감사를드립니다!
는 예외 중단 점을 추가하고 프로젝트를 실행, 그것은 것 충돌이 일어나는 라인에서 멈추십시오. 그건 당신이 문제를 격리 도움이 될 것입니다 – user1046037