简体   繁体   中英

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

Considering these facts:

1) It is better to initialize objects as let (immutable) rather than var whenever possible.

2) Some objects can throw when you call their initializer (such as AVAudioPlayer, for example ).

3) Some objects have initializers that require arguments that are not available before "self is available," once again such as AVAudioPlayer, so if they are to be a class-level/non-local let, then they must be initialized not before or after, but only inside init().

So, for example, let's say we have a class where we want to define a class-level property of type AVAudioPlayer (or any other object whose init method throws), but we want it to be a let.

After much trial and error, negotiating my way around X-Code warnings, and experimenting with optionals, the best I have come up with (with help) is this:

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()
    }

}

So, now basically we have an immutable value which may be nil if AVAudioPlayer.init() throws... but at least it is a let, and since it is set as an optional, the app won't crash if AVAudioPlayer.init() throws.

Is there a better way of doing this?

What is the best practice for initializing these types of objects (as a class level let)?

Thanks so much for your time and consideration! XD

Basically you have three options:

  1. forced try ( try! ), if you're 100% sure the AVAudioPlayer will succeed. Note, however that even if the url has valid contents, the initializer might still fail due to some other internal conditions
  2. mark the properties as optionals (like you did), this will guard you against failures but will increase the complexity of the rest of your code as you'll need to always unwrap the properties.
  3. mark your initializer as throwing. This will move upstream the burden of dealing with errors, but still at some point you're likely need to use the #1/#2 approaches .

All above solutions involve a compromise somewhere, as you'll need to handle initialization failures, sooner or later. The question is how do you want to do this:

  • crash the app since AVAudioPlayer failure is not recoverable in your app
  • handle the initialization failure and provide another path through the app
  • silently ignore the failures and keep on going with the app

And this all depends on the specifics of your app.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM