如何正确初始化 init() 抛出的非本地不可变对象?

How to Properly Initialize Non-Local Immutable Whose init() Throws?

考虑到这些事实:

1) 尽可能将对象初始化为 let(不可变)而不是 var。

2) 某些对象在调用它们的初始化程序时可能会抛出异常(例如 AVAudioPlayer,例如)。

3) 一些对象的初始化器需要在 "self is available," 之前不可用的参数,例如 AVAudioPlayer,所以如果它们是 class-level/non-local let,那么它们必须被初始化,而不是之前或之后,但仅在 init() 内部。

因此,例如,假设我们有一个 class,我们想要定义一个 class 级别 属性 类型的 AVAudioPlayer(或任何其他其 init 方法抛出异常的对象) ), 但我们希望它是一个 let。

经过多次试验和错误,协商解决 X 代码警告的方法,并尝试使用可选选项,我想出的最好的(在帮助下)是这样的:

import AVFoundation

class MySoundPoolPlayer {

    private let dingURL = Bundle.main.url(forResource: "ding", withExtension: "wav")
    private let dongURL = Bundle.main.url(forResource: "dong", withExtension: "wav")

    private let dingPlayer: AVAudioPlayer?
    private let dongPlayer: AVAudioPlayer?

    init() {

        dingPlayer = try? AVAudioPlayer(contentsOf: dingURL!)
        dongPlayer = try? AVAudioPlayer(contentsOf: dongURL!)

    }

    func playDing() {
        dingPlayer!.play()
    }

    func playDong() {
        dongPlayer!.play()
    }

    func stopAllSounds() {
        dingPlayer!.stop()
        dongPlayer!.stop()
    }

}

所以,现在基本上我们有一个不可变的值,如果 AVAudioPlayer.init() 抛出,它可能是 nil...但至少它是一个 let,并且由于它被设置为可选,应用程序赢了如果 AVAudioPlayer.init() 抛出则崩溃。

有更好的方法吗?

初始化这些类型的对象的最佳做法是什么(作为 class 级别让)?

非常感谢您的时间和考虑! XD

基本上你有三个选择:

  1. 强制尝试 (try!),如果您 100% 确定 AVAudioPlayer 会成功。但是请注意,即使 url 具有有效内容,初始化程序仍可能由于其他一些内部条件
  2. 而失败
  3. 将属性标记为可选(就像您所做的那样),这将防止您失败,但会增加其余代码的复杂性,因为您需要始终解包属性。
  4. 将您的初始化程序标记为抛出。这将把处理错误的负担转移到上游,但在某些时候您可能仍然需要使用#1/#2 方法。

以上所有解决方案都涉及到某处的妥协,因为您迟早需要处理初始化失败。问题是你想怎么做:

  • 应用程序崩溃,因为 AVAudioPlayer 失败在您的应用程序中无法恢复
  • 处理初始化失败并提供另一条通过应用程序的路径
  • 默默地忽略失败并继续使用应用程序

这完全取决于您应用的具体情况。