Swift 尝试初始化AVAudioPlayer后,因未包装的nil值而崩溃

Swift 尝试初始化AVAudioPlayer后,因未包装的nil值而崩溃,swift,console,Swift,Console,这是它在控制台上的说明: 可选的(file:///Users/david/Library/Developer/CoreSimulator/Devices/12D7F78E-5FFA-4964-9AEE-F395D962264F/data/Containers/Data/Application/A5B7545C-2800-495C-B016-07C3DF010D97/Documents/my_audio.wav) 致命错误:在打开可选值时意外发现nil,您的代码充满了新手错误。有些人对崩溃负有直接

这是它在控制台上的说明:

可选的(file:///Users/david/Library/Developer/CoreSimulator/Devices/12D7F78E-5FFA-4964-9AEE-F395D962264F/data/Containers/Data/Application/A5B7545C-2800-495C-B016-07C3DF010D97/Documents/my_audio.wav)
致命错误:在打开可选值时意外发现nil,您的代码充满了新手错误。有些人对崩溃负有直接责任,有些人迟早会产生问题,有些人只是没有跟上良好的编程实践

一切都在评论中解释

如果没有其他问题,请确保理解“主要错误2”,因为这与ViewController生命周期有关。序列标识符的大小写错误也是一个严重错误

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

以下代码在我的Xcode 6.4/iPhone模拟器8.4上运行良好。我遗漏了样板文件
AppDelegate
code。还要确保把我在下面遗漏的“导入”语句放回原处

//
//  playSoundsViewController.swift
//  recordVoice
//
//  Created by david on 11/08/15.
//  Copyright (c) 2015 Tomcorp. All rights reserved.
//

import UIKit
import AVFoundation


class playSoundsViewController: UIViewController {

    var audioPlayer:AVAudioPlayer!
    var receivedAudio:RecordedAudio!

    override func viewDidLoad() {
        super.viewDidLoad()

        audioPlayer = AVAudioPlayer(contentsOfURL: receivedAudio.filePathUrl, error: nil)
        audioPlayer.enableRate = true
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


    @IBAction func slowVoice(sender: UIButton) {
        audioPlayer.stop()
        audioPlayer.rate = 0.5
        audioPlayer.currentTime = 0.0
        audioPlayer.play()
    }

    @IBAction func fastVoice(sender: UIButton) {
            audioPlayer.stop()
            audioPlayer.rate = 2.0
            audioPlayer.currentTime = 0.0
            audioPlayer.play()
    }


    @IBAction func stopButtonSounds(sender: UIButton) {
            audioPlayer.stop()
    }

    }

    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        // Get the new view controller using segue.destinationViewController.
        // Pass the selected object to the new view controller.
    }
    */


//
//  recordSoundsViewController.swift
//  recordVoice
//
//  Created by david on 11/08/15.
//  Copyright (c) 2015 Tomcorp. All rights reserved.
//

import UIKit
import AVFoundation


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()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    override func viewWillAppear(animated: Bool) {

        stopButton.hidden = true
        recordButton.enabled = true

    }

    @IBAction func recordButton(sender: UIButton) {

        recordButton.enabled = false
        stopButton.hidden = false
        recordingLabel.hidden = false
        //record voice



        //Inside func recordAudio(sender: UIButton)
        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)

        var session = AVAudioSession.sharedInstance()
        session.setCategory(AVAudioSessionCategoryPlayAndRecord, error: nil)

        audioRecorder = AVAudioRecorder(URL: filePath, settings: nil, error: nil)
        audioRecorder.delegate = self
        audioRecorder.meteringEnabled = true
        audioRecorder.record()

    }

    func audioRecorderDidFinishRecording(recorder: AVAudioRecorder!, successfully flag: Bool) {
        if(flag){
        recordedAudio = RecordedAudio()
        recordedAudio.filePathUrl = recorder.url
        recordedAudio.title = recorder.url.lastPathComponent
        self.performSegueWithIdentifier("stopRecording", sender: recordedAudio)
        }else {
            println("Recording was not successfully")
            recordButton.enabled = true
            stopButton.hidden = true
        }
    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if (segue.identifier == "StopRecording"){
            let playSoundsVC:playSoundsViewController = segue.destinationViewController as! playSoundsViewController
            let data = sender as! RecordedAudio
            playSoundsVC.receivedAudio = data

        }
    }

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

}

//
//  RecordedAudio.swift
//  recordVoice
//
//  Created by david on 13/08/15.
//  Copyright (c) 2015 Tomcorp. All rights reserved.
//

import Foundation

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

//
//  AppDelegate.swift
//  recordVoice
//
//  Created by david on 11/08/15.
//  Copyright (c) 2015 Tomcorp. All rights reserved.
//

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.
        return true
    }

    func applicationWillResignActive(application: UIApplication) {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    }

    func applicationDidEnterBackground(application: UIApplication) {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    }

    func applicationWillEnterForeground(application: UIApplication) {
        // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
    }

    func applicationDidBecomeActive(application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }

    func applicationWillTerminate(application: UIApplication) {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    }


}

这是错误的部分这是错误的部分:audioPlayer=AVAudioPlayer(contentsOfURL:receivedAudio.filePathUrl,error:nil)您好thnx谢谢您的评论,但它不起作用我不知道为什么它说这是一个致命错误:在打开可选值时意外发现nil您替换了viewDidLoad()用我的,但还是犯了那个错误?在哪一行?我在代码的不同部分出错了每个录音机都让我换成了录音机!然后工作但不复制录音声音将“audioRecorder”更改为“audioRecorder”?把某物换成同样的东西?你希望我们怎么理解?您需要发布所有代码。理想情况下,将整个项目压缩并发布链接。啊,没关系。我懂了。因为我将audioPlayer(不是audioRecorder)的声明更改为显式可选(“?”vs“!”),所以在使用它时必须将其打开。是的,这是故意强迫你检查零,或强制展开,因为你仍然在学习选项。无论如何,在上下文中查看所有内容仍然是最有效的,即发布整个zip文件,包括声音资源。如果您是Swift/Xcode新手,各种错误可能是导致失败行为的原因。
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!
}