尝试初始化AVAudioPlayer后崩溃并显示未包装的nil值

时间:2015-08-14 17:39:43

标签: swift console

app.addEventListener('dom-change', function() {
    console.log('Our app is ready to rock!');

    var pages = document.querySelector("#pages");
    var next = document.querySelector("#next");
    next.addEventListener("click", function(){
        // choose an option here
        pages.selectNext();
        pages.selected = 2;
        pages.selected = pages.selected +1;  
     });
});

这就是它在控制台上的说法:

可选(文件:///用户/大卫/库/开发商/ CoreSimulator /设备/ 12D7F78E-5FFA-4964-9AEE-F395D962264F /数据/容器/数据/应用/ A5B7545C-2800-495C-B016-07C3DF010D97 /文件/ my_audio.wav) 致命错误:在解包可选值时意外发现nil

1 个答案:

答案 0 :(得分:0)

TOMCORP,你的代码充满了新手错误。有些人直接负责崩溃,有些人迟早会出现问题,有些人只是无法跟上良好的编程习惯。

所有内容都在评论中解释。

如果没有别的东西可以理解" MAJOR ERROR 2"因为这与ViewController生命周期有关。在segue标识符上获取大写错误也是一个严重的错误。

顺便说一下,似乎没有从播放VC返回的规定。您需要某种执行dismissViewControllerAnimated()的按钮。

以下代码在我的Xcode 6.4 / iPhone模拟器8.4上运行良好。我遗漏了样板AppDelegate代码。另外一定要放回' import'我在下面留下的陈述。

import UIKit
import AVFoundation

// FIX of Error 11
let stopRecordingSegue = "stopRecording"

// Error 1: (Style) Class names should always be capitalized
class PlaySoundsViewController: UIViewController {
    var audioPlayer: AVAudioPlayer?

    // MAJOR ERROR 2: viewDidLoad() runs **BEFORE** prepareForSegue(). So it was looking for the
    // 'recordedAudio' instance variable but prepareForSegue had NOT YET had a chance to
    // initialize it.

    // viewDidLoad is the WRONG place for DYNAMIC, RUNTIME initialization.

    // Instead we make a custom method, called by prepareForSegue(), so we're in full control
    // of the flow of data from VC to VC.
    func prepareAudio(receivedAudio: RecordedAudio) {
        var err: NSError?
        // Error 3: check carefully for all error conditions
        if let ap = AVAudioPlayer(contentsOfURL: receivedAudio.filePathUrl, error: &err) {
            audioPlayer = ap
            ap.enableRate = true
        }
        else {
            assertionFailure("Exiting program, couldn't create recorder: \(err?.localizedDescription)")
        }
    }

    // Error 4: (Style) Don't have overridden methods that do nothign but call super().
    // The clutter just creates confusion. didRecieveMemoryWarning function removed.

    @IBAction func slowVoice(sender: UIButton) {
        // Error 5: Handle case where player did not get initialized
        if let ap = audioPlayer {
            ap.stop()
            ap.rate = 0.5
            ap.currentTime = 0.0
            ap.play()
        }
        else {
            println("No audioplayer available in slowVoice!")
        }
    }

    @IBAction func fastVoice(sender: UIButton) {
        // Error 5: Handle case where player did not get initialized
        if let ap = audioPlayer {
            ap.stop()
            ap.rate = 2.0
            ap.currentTime = 0.0
            ap.play()
        }
        else {
            println("No audioplayer available in fastVoice!")
        }
    }

    @IBAction func stopButtonSounds(sender: UIButton) {
        // Error 5: Handle case where player did not get initialized
        if let ap = audioPlayer {
            ap.stop()
        }
        else {
            println("No audioplayer available in stop!")
        }
    }

    // Error 6: (Style) Leave out dead code that's commented out. Creates more confusing clutter.
}

class RecordSoundsViewController: UIViewController, AVAudioRecorderDelegate {
    @IBOutlet weak var recordButton: UIButton!
    @IBOutlet weak var recordingLabel: UILabel!
    @IBOutlet weak var stopButton: UIButton!

    var audioRecorder: AVAudioRecorder!
    var recordedAudio: RecordedAudio!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Error 7: Initialization: All one-time initialization must go in viewDidLoad(), not when the user
        // pressed a button. Sometimes redundant initialization is not actually harmful, but it's a terrible
        // programming habit nonetheless.
        let dirPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! String

        let recordingName = "my_audio.wav"
        let pathArray = [dirPath, recordingName]
        let filePath = NSURL.fileURLWithPathComponents(pathArray)
        println(filePath)

        // Error 8: Don't create unnecessary variables just to do one-time initialization. Confusing clutter.
        AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord, error: nil)

        // Error 9: Don't use 'nil' for error object; receive the error and check it
        var err: NSError?
        audioRecorder = AVAudioRecorder(URL: filePath, settings: nil, error: &err)
        if let e = err {
            assertionFailure("Exiting program, couldn't create recorder: \(e.localizedDescription)")
        }
        else {
            println("successfully created audio recorder")
            audioRecorder.delegate = self
            audioRecorder.meteringEnabled = true
        }
    }

    override func viewWillAppear(animated: Bool) {
        // Error 10: overriding conventions: Failure to call super of a built-in method like this one
        super.viewWillAppear(animated)
        stopButton.hidden = true
        recordButton.enabled = true
    }

    @IBAction func recordButton(sender: UIButton) {
        recordButton.enabled = false
        stopButton.hidden = false
        recordingLabel.hidden = false

        audioRecorder.record()
    }

    func audioRecorderDidFinishRecording(recorder: AVAudioRecorder!, successfully flag: Bool) {
        if flag {
            recordedAudio = RecordedAudio()
            recordedAudio.filePathUrl = recorder.url
            recordedAudio.title = recorder.url.lastPathComponent
            // Error 11: sender should be the actual initiating VC
            self.performSegueWithIdentifier(stopRecordingSegue, sender: self)
        }
        else {
            println("Recording was not successful") // Error 12: Grammar
            recordButton.enabled = true
            stopButton.hidden = true
        }
    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        // Error 13: Not using a defined constant string, you've messed up capitalization
        if segue.identifier == stopRecordingSegue {
            let playSoundsVC = segue.destinationViewController as! PlaySoundsViewController
            // REST OF FIX OF MAJOR ERROR 2: send in data to a method so runtime initialization code can run
            playSoundsVC.prepareAudio(recordedAudio)
        }
    }

    @IBAction func stopButton(sender: UIButton) {
        recordButton.enabled = true
        recordingLabel.hidden = true
        audioRecorder.stop()
        AVAudioSession.sharedInstance().setActive(false, error: nil)
    }
}

class RecordedAudio: NSObject{
    var filePathUrl: NSURL!
    var title: String!
}