-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 ?? "")
}
}
無關,但同時也很難效仿,它看起來像有就是你比你先恢復計時器執行的路徑設置處理程序和時間表... – Rob
嗨,羅布,只是爲了澄清,我有一個updateTimer.resume()在toggleRecordButton IBAction塊的開始。你是否建議在處理程序和時間表之前恢復計時器是錯誤的? – elarcoiris
是的,我建議你在啓動之前配置定時器。如果在完成配置之前啓動它,您希望它做什麼?如果你幸運的話,什麼都不會出錯,但爲什麼依賴這種無證的行爲呢?在嘗試啓動之前完成配置是最安全的。 – Rob