【问题标题】:xcode9 swift4 Fatal error: Unexpectedly found nil while unwrapping an Optional valuexcode9 swift4 致命错误:在展开可选值时意外发现 nil
【发布时间】:2018-05-05 09:38:13
【问题描述】:

我正在尝试在应用启动时播放 mp3 文件(5 秒),但我不断收到错误消息:

线程 1:致命错误:在展开可选值时意外发现 nil

import UIKit
import AudioToolbox

class ViewController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    //create SystemSoundID
    var soundID:SystemSoundID = 0
    let path = Bundle.main.path(forResource: "iPhone100Voice", ofType: "mp3")
    let baseURL = NSURL(fileURLWithPath: path!)
    AudioServicesCreateSystemSoundID(baseURL, &soundID)
    //play
    AudioServicesPlaySystemSound(soundID)

    }

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

}

谁能解释一下为什么以及如何解决这个问题?

【问题讨论】:

  • if let path = Bundle.main.path(forResource: "iPhone100Voice", ofType: "mp3"){ }
  • let baseURL = NSURL(fileURLWithPath: path!) - 你应该避免强制解包可选值,即使“相信”它们永远不会 - 相反,你应该始终防范它们并有条件处理不可预见的情况
  • 文件iPhone100Voice.mp3 不在捆绑包中。并使用 API url(forResource: withExtension:。这避免了创建 URL 的额外步骤。
  • @MadProgrammer 在这种特殊情况下,应该强制解开可选值,因为崩溃会显示设计错误。如果文件在包中,则代码不能崩溃,因为文件不能在运行时更改。
  • @vadian 然后它留在代码中 - 还有其他选项

标签: ios iphone swift


【解决方案1】:

检查这几点,所有条件都必须为真:

  • 文件是否在项目导航器中?
  • 文件检查器中是否选中了Target Membership 复选框(要让它选择文件并按⌥⌘1
  • 文件名拼写是否正确 (iPhone100Voice.mp3)?

【讨论】:

    【解决方案2】:

    请检查您的文件名,可能文件名不正确或不在捆绑包中

    所以请检查带有扩展名的文件名。

    使用if letguardunwrap的路径:

    override func viewDidLoad() {
    
        super.viewDidLoad()
        var soundID:SystemSoundID = 0
    
        if let path = Bundle.main.path(forResource: "iPhone100Voice", ofType: "mp3") {
           let baseURL = NSURL(fileURLWithPath: path)
           AudioServicesCreateSystemSoundID(baseURL, &soundID)
           AudioServicesPlaySystemSound(soundID)
        } else {
            let alert = UIAlertController(title: "Alert", message: "File not found in your bundle", preferredStyle: UIAlertControllerStyle.alert)
            alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.cancel, handler: nil))
            self.present(alert, animated: true, completion: nil)
    
       }
    }
    

    【讨论】:

    • 这样可以避免崩溃,但如果文件不存在则不会播放任何内容。
    • @vadian:谢谢,但我们应该避免所有崩溃
    • 再次强调:文件必须在设计/构建时包含在包中。它不能在运行时更改。所以代码不能崩溃。
    • @vadian:但是文件名可能不正确,所以会发生崩溃
    • 是的,但是开发人员应该在调试应用程序时解决这个问题,而不是在运行时进行检查。用力打开包装一下子就发现了设计错误。
    【解决方案3】:

    播放视频可能对您有帮助。在将视频文件添加到项目中时,请确保在文件检查器中选中或未选中目标复选框。

    override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(true)
    
    
            if  let path = Bundle.main.path(forResource: "video_1523966087401 (1)", ofType: ".mp4"){
                let player = AVPlayer(url: URL(fileURLWithPath: path))
                let playerController = AVPlayerViewController()
                playerController.player = player
                present(playerController, animated: true) {
                    player.play()
                }
            }else{
                //show dialog to user
    
                print("Error: File not found")
            }
        }
    

    【讨论】:

    • 这样可以避免崩溃,但如果文件不存在则不会播放任何内容。
    • 如果文件不存在如何播放视频,这是不可能的。
    • 是的,“if let”条件会避免崩溃,如果视频路径存在则播放视频,否则不播放。
    • 是的,但是丢失的文件崩溃会提醒开发人员修复问题。使用您的解决方案,开发人员可能不会注意到文件丢失,应用程序被释放并且用户抱怨他们为什么听不到声音。这是更好的用户体验吗?强制展开以发现设计错误非常有帮助。
    • @Vadian,对不起,我也必须给出错误,我更新了我的代码
    【解决方案4】:

    ! 表示崩溃运算符,它解开可选值。比如说,那里的价值就像 Optional(53) 一样。因此,当path! 尝试解包可选并将值 53 附加到路径变量中时,该变量应该包含除 nil 之外的值。

    要解决这个问题,我们可以使用两种方法

    使用守卫,

    guard let pathValue = path else {
            return
    }
    
    let baseURL = NSURL(fileURLWithPath: pathValue) //no need to use pathValue, if you use then you have to use path!, although it makes no crash now.  
    AudioServicesCreateSystemSoundID(baseURL, &soundID)
    //play
    AudioServicesPlaySystemSound(soundID)
    

    如果path为nil,则进入block并返回,如果有value,则继续下一行。

    使用 if-let,知道optional binding

    if let pathValue = path {
        let baseURL = NSURL(fileURLWithPath: pathValue) //same comment
        AudioServicesCreateSystemSoundID(baseURL, &soundID)
        //play
        AudioServicesPlaySystemSound(soundID)
    } 
    

    现在,如果路径是可选的,则无法到达块并传递到下一行

    【讨论】:

      猜你喜欢
      • 2016-01-26
      • 2016-02-29
      • 2020-09-19
      • 2016-01-08
      相关资源
      最近更新 更多