Reputation: 12638
The Swift documentation Initialization: Failable Initializers details how to use init?
to create a failable initializer, which is an initializer that returns an optional of the type it initializes. As with all optionals, it could be nil
or non-nil
.
The docs also mention you can use init!
to create a failable initializer, which returns an implicitly unwrapped optional of the type it initializes (see the The init! Failable Initializer section). This unwraps the optional and indicates it "should" be non-nil
. If it is nil
and you accessed it without checking, something a programmer may skip since it is marked as "should be non-nil
", a runtime error will be generated.
Two questions:
init!
instead of init?
when implementing a failable initializer?init!
always returns an implicitly unwrapped optional which "should" be non-nil
, why wouldn't you just use init
instead of init!
?Upvotes: 3
Views: 380
Reputation: 332
You can delegate from init? to init! and vice versa, and you can override init? with init! and vice versa.
That means you can convert some init?
to init!
if you are pretty sure it won't fail or vice versa. In the documentation there is example of:
enum TemperatureUnit {
case Kelvin, Celsius, Fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil
}
}
}
You could have some similar code and you could initialize it only with constants, not using user input, or some other sources of input. So you are pretty sure it won't fail, why not make it init!
?
Upvotes: 1
Reputation: 72760
You can use it in classes to override a failable initializer and make it non failable.
The same caution must be applied when dealing with implicitly unwrapped: it can be dangerous because it has the power to make the app crash.
Upvotes: 0
Reputation: 299455
In the vast majority of cases you should use init
rather than init!
. There are a few cases where init!
is necessary. The most common in my experience is when a "must succeed" initializer wants to call a failable initializer. Consider this case:
struct NotEmpty {
let something: String
init?(something: String) {
guard !something.isEmpty else { return nil }
self.something = something
}
init() {
self.init(something: "unknown")! // <-- This is illegal
}
}
We know that init()
will succeed because we're passing a non-empty string. But there's no way to express that with a standard initializer (and the compiler can't prove it's true anyway). Instead, we need to use an init!
:
init!() {
self.init(something: "unknown")
}
Now the caller can treat the result as though it were non-optional (which it really is), even though according to the types it could have failed. That would be a programming error and you'd crash. The !
here basically says "yeah, I know it could fail, but I promise it never will." And in Swift, "promise" means "or else please crash."
Upvotes: 6