2017-12-03 823 views
-2

我已經實現了使用DispatchSource和計時器工作的錄製音頻(錄製和播放工作正常)的計時器,但是當我按下我的導航控制器選項卡上的後退按鈕時,應用程序崩潰。我可以毫無問題地前進到下一個屏幕。 錯誤:線程1:EXC_BAD_INSTRUCTION(code = EXC_I386_INVOP,subcode = 0x0)DispatchSource.makeTimerSource當按下後退按鈕時崩潰應用程序Swift

有人請給我一個暗示,爲什麼這是?

我的看法控制器代碼:

import Foundation 
import UIKit 
import AVFoundation 

class RecordGreetingController: UIViewController { 

@IBOutlet weak var timeLabel: UILabel! 
var audioPlayer: AVAudioPlayer! 
var updateTimer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.main) 

override func viewDidLoad() { 
    super.viewDidLoad() 
} 

@IBAction func toggleRecordButton(_ sender: UIButton) { 
    if AudioRecorderManager.shared.recorder == nil { 
     if AudioRecorderManager.shared.recordAudio(fileName: "NewRecording") { 
      updateTimer.resume() 
     } 

     let formatter = DateComponentsFormatter() 
     formatter.zeroFormattingBehavior = .pad 
     formatter.includesApproximationPhrase = false 
     formatter.allowedUnits = [.minute, .second] 
     formatter.calendar = Calendar.current 

     updateTimer.schedule(deadline: DispatchTime.now(), repeating: DispatchTimeInterval.milliseconds(100)) 

     updateTimer.setEventHandler { [weak self] in 
      self?.timeLabel.text = formatter.string(from: AudioRecorderManager.shared.recorder!.currentTime) 
     } 
    } 
    else { 
     AudioRecorderManager.shared.finishRecording() 
     AudioRecorderManager.shared.recorder = nil 
     updateTimer.suspend() 
    } 
} 

}

我AudioRecorderManager類:

import Foundation 
import AVFoundation 

class AudioRecorderManager: NSObject, AVAudioRecorderDelegate { 


static let shared = AudioRecorderManager() 

var recordingSession: AVAudioSession! 
var recorder: AVAudioRecorder? 


func setup() { 

    recordingSession = AVAudioSession.sharedInstance() 

    do { 
     try recordingSession.setCategory(AVAudioSessionCategoryPlayAndRecord, with: .defaultToSpeaker) 
     try recordingSession.setActive(true) 

     // Request permission from user 
     recordingSession.requestRecordPermission{ (allowed) in 
      if allowed { 
       print("Mic authorised") 
      } 
      else { 
       print("Mic not authorised") 
       // TODO display alert to allow microphone access in settings to operate 
      } 
     } 
    } 
    catch { 
     print("Failed to set category", error.localizedDescription) 
    } 
} 

var meterTimer: Timer? 
var recorderApc0: Float? = 0 
var recorderPeak0: Float? = 0 

// Starts the recording session 
func recordAudio(fileName:String) -> Bool { 
    let url = getUserPath().appendingPathComponent(fileName+".m4a") 
    let audioURL = URL.init(fileURLWithPath: url.path) 
    let recordSettings: [String: Any] = [ 
     AVFormatIDKey: NSNumber(value: kAudioFormatAppleLossless), 
     AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue, 
     AVEncoderBitRateKey: 12000.0, 
     AVNumberOfChannelsKey: 1, 
     AVSampleRateKey: 44100.0 
    ] 

    do { 
     recorder = try AVAudioRecorder(url: audioURL, settings: recordSettings) 
     recorder?.delegate = self 
     recorder?.isMeteringEnabled = true 
     recorder?.prepareToRecord() 
     recorder?.record() 

     self.meterTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { (timer: Timer) in 
      // Update recorder meter values 
      if let recorder = self.recorder { 
       recorder.updateMeters() 
       self.recorderApc0 = recorder.averagePower(forChannel: 0) 
       self.recorderPeak0 = recorder.peakPower(forChannel: 0) 
      } 
     }) 
     print("Recording") 
     return true 
    } 
    catch { 
     print("Error recording") 
     return false 
    } 
} 

// Stop recording 
func finishRecording() { 
    self.recorder?.stop() 
    self.meterTimer?.invalidate() 
} 

// Gets path for the folder the file is being saved to 
func getUserPath() -> URL { 
    return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] 
} 

func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) { 
    print("Audio Manager did finish recording", flag) 
} 

func audioRecorderEncodeErrorDidOccur(_ recorder: AVAudioRecorder, error: Error?) { 
    print("Error encoding ", error?.localizedDescription ?? "") 
} 

}

+0

無關,但同時也很難效仿,它看起來像有就是你比你先恢復計時器執行的路徑設置處理程序和時間表... – Rob

+0

嗨,羅布,只是爲了澄清,我有一個updateTimer.resume()在toggleRecordButton IBAction塊的開始。你是否建議在處理程序和時間表之前恢復計時器是錯誤的? – elarcoiris

+0

是的,我建議你在啓動之前配置定時器。如果在完成配置之前啓動它,您希望它做什麼?如果你幸運的話,什麼都不會出錯,但爲什麼依賴這種無證的行爲呢?在嘗試啓動之前完成配置是最安全的。 – Rob

回答

-1

這最終被解決我的問題。忽略來自上述兩個誰沒有解決這個問題的一個心懷不滿的向下投票:

var updateTimer: DispatchSourceTimer? 
@IBOutlet weak var timeLabel: UILabel! 

func startTimer() { 
    let formatter = DateComponentsFormatter() 
    formatter.zeroFormattingBehavior = .pad 
    formatter.includesApproximationPhrase = false 
    formatter.allowedUnits = [.minute, .second] 
    formatter.calendar = Calendar.current 
    let queue = DispatchQueue.main 
    updateTimer = DispatchSource.makeTimerSource(queue: queue) 
    updateTimer!.schedule(deadline: .now(), repeating: .milliseconds(100)) 
    updateTimer!.setEventHandler { [weak self] in 
     self?.timeLabel.text = formatter.string(from: AudioRecorderManager.shared.recorder!.currentTime) 
    } 
    updateTimer!.resume() 
} 

func stopTimer() { 
    updateTimer?.cancel() 
    updateTimer = nil 
} 

deinit { 
    self.stopTimer() 
} 

@IBAction func toggleRecordButton(_ sender: UIButton) { 
    if AudioRecorderManager.shared.recorder == nil { 
     if AudioRecorderManager.shared.recordAudio(fileName: "NewRecording") { 
       startTimer() 
     } 
    } 
    else { 
     stopTimer() 
     AudioRecorderManager.shared.finishRecording() 
     AudioRecorderManager.shared.recorder = nil 
    } 
} 
相關問題