Convenience Init 是否需要与 Required Init 具有相同数量的参数
Does Convenience Init need to have same number of parameters as Required Init
发件人: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.
我对 convenience init 的理解基本上是作为“典型”输入参数的快捷方式。大多数时候,convenience init()
中的参数个数与required init()
中的参数个数相同
例如:
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
}
}
我相信在某些情况下 convenience init
的参数数量会比 required init
少,因为一些默认值会到位。
我认为以下内容有效? required init
中有一个附加参数,但 convenience init
中没有。但是,在这种情况下,我如何访问 speed
参数并将值传递给 speed
参数?
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
}
}
指定(甚至是必需)初始化器和便利初始化器之间的参数数量没有相关性。
便利初始化器只是那些需要将初始化转发给指定初始化器之一的初始化器。它有多少输入参数完全取决于实现和用例。可能大于、小于或等于。
很难找到一个足够简单的例子来演示所有这些,但请考虑这样的事情:
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[[=10=]] 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)
}
}
这里我创建了一个 class 和 2 个指定的初始值设定项。这两个需要设置默认情况下尚未设置的所有属性。这意味着他们需要同时设置 words
和 highlightedWord
。一个指定的初始化器不能将调用委托给另一个指定的初始化器,因此以下将不起作用:
init(words: [String], highlightedIndex: Int) {
self.init(words: words, highlighted: words[highlightedIndex])
}
并且所有便利初始化器都需要调用任何指定的初始化器,并且在调用指定的构造函数之前也不能直接设置或使用 self
属性。所以:
convenience init(wordsSeparatedByWhitespace: String, highlightedIndex: Int) {
let words = wordsSeparatedByWhitespace.components(separatedBy: .whitespaces)
// print(highlightedWord) // NOT OK!
self.init(words: words, highlightedIndex: highlightedIndex)
print(highlightedWord)
}
并且我希望从这个例子中可以清楚地看出便利初始化器可以有更多、更少或相同数量的输入参数。这一切都取决于。
一些更合理的例子:
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) }
}
还有; required
关键字与此上下文中的任何内容无关。您可以将它放置到任何初始化器(指定或方便)、多个甚至所有初始化器中。
根据评论添加附加信息:
your examples shows that you can have a convenience init that can pass
parameters back to the required init``. what about instances where I
there is no convenience init``` that can pass a value to parameter to
one which is in required init but not in the convenience init. Like my
speed parameter in my original question.
这实际上取决于您的 class 的界面,您的示例中没有完全显示。例如,speed
public是否暴露?
如果速度是 public,那么您正在寻找的是
let itemInstance = MyItem()
itemInstance.timeStamp = timeStamp
itemInstance.position = position
itemInstance.distance = distance
itemInstance.speed = speed
或
let itemInstance = MyItem(timeStamp: timeStamp, position: position, distance: distance)
itemInstance.speed = speed
甚至创建一个新的便捷构造函数
extension MyItem {
convenience init(timeStamp: FitTime? = nil,
position: Position? = nil,
distance: Measurement<UnitLength>? = nil
speed: Speed? = nil
) {
self.init(timeStamp: timeStamp, position: position, distance: distance)
self.speed = speed
}
}
let itemInstance = MyItem(timeStamp: timeStamp, position: position, distance: distance, speed: speed)
但是,如果您的 speed
属性 是私有定义的,那么您只能在内部分配给它。您需要能够修改 class 以便它可以在某处接受此 属性。通过构造函数,通过 属性 或通过方法。在完全没有访问权限之前,您不能指望能够修改它。
但如果这是你自己的 class 如果可能的话,我会把事情颠倒过来。您的主要构造函数应该是接受所有 4 个参数的构造函数。但是应该需要您的便利初始值设定项(是的,可能需要便利初始值设定项)。所以在那种情况下你会:
public required convenience init() {
self.init(timeStamp: self, position: self, distance: self, speed: self)
}
public init(timeStamp: FitTime? = nil,
position: Position? = nil,
distance: Measurement<UnitLength>? = nil,
speed: Speed?) {
super.init()
self.timeStamp = timeStamp
self.position = position
self.distance = distance
self.speed = speed
}
}
与您的代码相比,这个具体案例感觉它缺少了一些东西,但是同样,您没有提供 class 的完整定义,并且它确实或应该这样做。例如,在您提供的代码中,如果调用便利初始化程序,则以下行将按此顺序执行:
self.$timeStamp.owner = self // from self.init()
self.timeStamp = timeStamp
这看起来像是一个问题。
所以总结一下。有很多方法可以实现您的要求。但这完全取决于您要在此处构建的内容。
我希望它至少能让你走上正轨。
发件人: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.
我对 convenience init 的理解基本上是作为“典型”输入参数的快捷方式。大多数时候,convenience init()
中的参数个数与required init()
例如:
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
}
}
我相信在某些情况下 convenience init
的参数数量会比 required init
少,因为一些默认值会到位。
我认为以下内容有效? required init
中有一个附加参数,但 convenience init
中没有。但是,在这种情况下,我如何访问 speed
参数并将值传递给 speed
参数?
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
}
}
指定(甚至是必需)初始化器和便利初始化器之间的参数数量没有相关性。
便利初始化器只是那些需要将初始化转发给指定初始化器之一的初始化器。它有多少输入参数完全取决于实现和用例。可能大于、小于或等于。
很难找到一个足够简单的例子来演示所有这些,但请考虑这样的事情:
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[[=10=]] 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)
}
}
这里我创建了一个 class 和 2 个指定的初始值设定项。这两个需要设置默认情况下尚未设置的所有属性。这意味着他们需要同时设置 words
和 highlightedWord
。一个指定的初始化器不能将调用委托给另一个指定的初始化器,因此以下将不起作用:
init(words: [String], highlightedIndex: Int) {
self.init(words: words, highlighted: words[highlightedIndex])
}
并且所有便利初始化器都需要调用任何指定的初始化器,并且在调用指定的构造函数之前也不能直接设置或使用 self
属性。所以:
convenience init(wordsSeparatedByWhitespace: String, highlightedIndex: Int) {
let words = wordsSeparatedByWhitespace.components(separatedBy: .whitespaces)
// print(highlightedWord) // NOT OK!
self.init(words: words, highlightedIndex: highlightedIndex)
print(highlightedWord)
}
并且我希望从这个例子中可以清楚地看出便利初始化器可以有更多、更少或相同数量的输入参数。这一切都取决于。
一些更合理的例子:
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) }
}
还有; required
关键字与此上下文中的任何内容无关。您可以将它放置到任何初始化器(指定或方便)、多个甚至所有初始化器中。
根据评论添加附加信息:
your examples shows that you can have a convenience init that can pass parameters back to the required init``. what about instances where I there is no convenience init``` that can pass a value to parameter to one which is in required init but not in the convenience init. Like my speed parameter in my original question.
这实际上取决于您的 class 的界面,您的示例中没有完全显示。例如,speed
public是否暴露?
如果速度是 public,那么您正在寻找的是
let itemInstance = MyItem()
itemInstance.timeStamp = timeStamp
itemInstance.position = position
itemInstance.distance = distance
itemInstance.speed = speed
或
let itemInstance = MyItem(timeStamp: timeStamp, position: position, distance: distance)
itemInstance.speed = speed
甚至创建一个新的便捷构造函数
extension MyItem {
convenience init(timeStamp: FitTime? = nil,
position: Position? = nil,
distance: Measurement<UnitLength>? = nil
speed: Speed? = nil
) {
self.init(timeStamp: timeStamp, position: position, distance: distance)
self.speed = speed
}
}
let itemInstance = MyItem(timeStamp: timeStamp, position: position, distance: distance, speed: speed)
但是,如果您的 speed
属性 是私有定义的,那么您只能在内部分配给它。您需要能够修改 class 以便它可以在某处接受此 属性。通过构造函数,通过 属性 或通过方法。在完全没有访问权限之前,您不能指望能够修改它。
但如果这是你自己的 class 如果可能的话,我会把事情颠倒过来。您的主要构造函数应该是接受所有 4 个参数的构造函数。但是应该需要您的便利初始值设定项(是的,可能需要便利初始值设定项)。所以在那种情况下你会:
public required convenience init() {
self.init(timeStamp: self, position: self, distance: self, speed: self)
}
public init(timeStamp: FitTime? = nil,
position: Position? = nil,
distance: Measurement<UnitLength>? = nil,
speed: Speed?) {
super.init()
self.timeStamp = timeStamp
self.position = position
self.distance = distance
self.speed = speed
}
}
与您的代码相比,这个具体案例感觉它缺少了一些东西,但是同样,您没有提供 class 的完整定义,并且它确实或应该这样做。例如,在您提供的代码中,如果调用便利初始化程序,则以下行将按此顺序执行:
self.$timeStamp.owner = self // from self.init()
self.timeStamp = timeStamp
这看起来像是一个问题。
所以总结一下。有很多方法可以实现您的要求。但这完全取决于您要在此处构建的内容。
我希望它至少能让你走上正轨。