如何正确初始化 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
基本上你有三个选择:
- 强制尝试 (
try!
),如果您 100% 确定 AVAudioPlayer
会成功。但是请注意,即使 url 具有有效内容,初始化程序仍可能由于其他一些内部条件 而失败
- 将属性标记为可选(就像您所做的那样),这将防止您失败,但会增加其余代码的复杂性,因为您需要始终解包属性。
- 将您的初始化程序标记为抛出。这将把处理错误的负担转移到上游,但在某些时候您可能仍然需要使用#1/#2 方法。
以上所有解决方案都涉及到某处的妥协,因为您迟早需要处理初始化失败。问题是你想怎么做:
- 应用程序崩溃,因为
AVAudioPlayer
失败在您的应用程序中无法恢复
- 处理初始化失败并提供另一条通过应用程序的路径
- 默默地忽略失败并继续使用应用程序
这完全取决于您应用的具体情况。
考虑到这些事实:
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
基本上你有三个选择:
- 强制尝试 (
try!
),如果您 100% 确定AVAudioPlayer
会成功。但是请注意,即使 url 具有有效内容,初始化程序仍可能由于其他一些内部条件 而失败
- 将属性标记为可选(就像您所做的那样),这将防止您失败,但会增加其余代码的复杂性,因为您需要始终解包属性。
- 将您的初始化程序标记为抛出。这将把处理错误的负担转移到上游,但在某些时候您可能仍然需要使用#1/#2 方法。
以上所有解决方案都涉及到某处的妥协,因为您迟早需要处理初始化失败。问题是你想怎么做:
- 应用程序崩溃,因为
AVAudioPlayer
失败在您的应用程序中无法恢复 - 处理初始化失败并提供另一条通过应用程序的路径
- 默默地忽略失败并继续使用应用程序
这完全取决于您应用的具体情况。