2017-12-03 825 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 
    } 
} 
相关问题