From: https://docs.swift.org/swift-book/LanguageGuide/Initialization.html
Create convenience initializers whenever a shortcut to a common initialization pattern will save time or make initialization of the class clearer in intent.
My understanding of convenience init is basically as a shortcut to "typical" input parameters. Most of the time, the number of parameters in convenience init()
is the same as required init()
eg:
public required init() {
super.init()
self.$timeStamp.owner = self
self.$position = self
self.$distance = self
}
public convenience init(timeStamp: FitTime? = nil,
position: Position? = nil,
distance: Measurement<UnitLength>? = nil {
self.init()
self.timeStamp = timeStamp
self.position = position
self.distance = distance
}
}
I believe there are instances where convenience init
will have lesser # of parameters vs required init
as some default values will be put in place.
I think the below is valid? There is an additional parameter in required init
but is not present in convenience init
. However, in this case, how am I able to access and pass a value to the speed
parameter?
public required init() {
super.init()
self.$timeStamp.owner = self
self.$position = self
self.$distance = self
self.$speed = self // ADDED THIS
}
public convenience init(timeStamp: FitTime? = nil,
position: Position? = nil,
distance: Measurement<UnitLength>? = nil {
self.init()
self.timeStamp = timeStamp
self.position = position
self.distance = distance
}
}
CodePudding user response:
There is no correlation in number of parameters between designated (or even required) initializers and convenience initializers.
Convenience initializers are simply those that need to forward initialization to one of designated initializers. How many input parameters will it have is completely dependent on implementation and use-case. It may be more, less or equal.
It is hard to find a simple-enough example to demonstrate all of those but consider something like this:
class HighlightedWordContainer {
let words: [String]
var highlightedWord: String
init(words: [String], highlighted: String) {
self.words = words
self.highlightedWord = highlighted
}
init(words: [String], highlightedIndex: Int) {
self.words = words
self.highlightedWord = words[highlightedIndex]
}
convenience init(singleWord: String) {
self.init(words: [singleWord], highlighted: singleWord)
}
convenience init(word1: String, word2: String, word3: String, highlighted: String) {
self.init(words: [word1, word2, word3], highlighted: highlighted)
}
convenience init(wordsSeparatedByWhitespace: String, highlightedIndex: Int) {
let words = wordsSeparatedByWhitespace.components(separatedBy: .whitespaces)
self.init(words: words, highlightedIndex: highlightedIndex)
}
convenience init?(descriptor: [String: Any], keys: [String], selectedKey: String) {
let words: [String] = keys.compactMap { descriptor[$0] as? String }
guard words.isEmpty == false else { return nil }
guard let selectedWord = descriptor[selectedKey] as? String else { return nil }
guard let selectedWordIndex = words.firstIndex(of: selectedWord) else { return nil }
self.init(words: words, highlightedIndex: selectedWordIndex)
}
}
Here I created a class with 2 designated initializers. These two need to set all properties that are not already set by default. Which means they need to set both words
and highlightedWord
. A designated initializer may not delegate a call to another designated initializer so the following will not work:
init(words: [String], highlightedIndex: Int) {
self.init(words: words, highlighted: words[highlightedIndex])
}
And all convenience initializers do need to call any of the designated initializers and may also not directly set or use self
properties UNTIL a designated constructor is being called. So:
convenience init(wordsSeparatedByWhitespace: String, highlightedIndex: Int) {
let words = wordsSeparatedByWhitespace.components(separatedBy: .whitespaces)
// print(highlightedWord) // NOT OK!
self.init(words: words, highlightedIndex: highlightedIndex)
print(highlightedWord)
}
And from the example I hope it makes clear that a convenience initializer can have more, fewer or same number of input parameters. It all just depends.
Some more plausible examples:
class Point {
let x: Int
let y: Int
init(x: Int, y: Int) { self.x = x; self.y = y }
convenience init(x: Int) { self.init(x: x, y: 0) }
convenience init(y: Int) { self.init(x: 0, y: y) }
convenience init(polar: (radius: Double, angle: Double)) { self.init(x: Int(cos(polar.angle)*polar.radius), y: Int(sin(polar.angle)*polar.radius)) }
}
class NumericValueAsString {
let stringValue: String // A value represented as "123.456"
init(stringValue: String) { self.stringValue = stringValue }
convenience init(value: Int) { self.init(stringValue: .init(value)) }
convenience init(integerPart: String, fractionPart: String) { self.init(stringValue: integerPart "." fractionPart) }
}
Also; required
keyword has nothing to do with anything in this context. You can place it to any of the initializers (designated or convenience), to multiple of them or even all of them.