swift 中的 convenience init 与 init 有什么区别,最好有明确的例子
What is the difference between convenience init vs init in swift, explicit examples better
我很难理解两者之间的区别,或者 convenience init
的目的。
当你有一些 class 有很多属性时使用便利初始化器,这使得它有点 "Painful" 总是用所有变量初始化机智,所以你用便利初始化器做的是您只需传递一些变量来初始化 object,并为其余变量分配默认值。 Ray Wenderlich 网站上有一个非常好的视频,不确定它是否免费,因为我有一个付费帐户。
这是一个示例,您可以看到我没有用所有这些变量初始化我的 object,而是给它一个标题。
struct Scene {
var minutes = 0
}
class Movie {
var title: String
var author: String
var date: Int
var scenes: [Scene]
init(title: String, author: String, date: Int) {
self.title = title
self.author = author
self.date = date
scenes = [Scene]()
}
convenience init(title:String) {
self.init(title:title, author: "Unknown", date:2016)
}
func addPage(page: Scene) {
scenes.append(page)
}
}
var myMovie = Movie(title: "my title") // Using convenicence initializer
var otherMovie = Movie(title: "My Title", author: "My Author", date: 12) // Using a long normal initializer
标准 init
:
Designated initializers are the primary initializers for a class. A
designated initializer fully initializes all properties introduced by
that class and calls an appropriate superclass initializer to continue
the initialization process up the superclass chain.
convenience init
:
Convenience initializers are secondary, supporting initializers for a
class. You can define a convenience initializer to call a designated
initializer from the same class as the convenience initializer with some
of the designated initializer’s parameters set to default values. You can
also define a convenience initializer to create an instance of that class
for a specific use case or input value type.
简而言之,这意味着您可以使用便利构造器来使调用指定构造器更快、更“方便”。因此,便利初始化器需要使用 self.init
而不是您可能在指定初始化器的覆盖中看到的 super.init
。
伪代码示例:
init(param1, param2, param3, ... , paramN) {
// code
}
// can call this initializer and only enter one parameter,
// set the rest as defaults
convenience init(myParamN) {
self.init(defaultParam1, defaultParam2, defaultParam3, ... , myParamN)
}
我在创建自定义视图时经常使用这些,这些视图有很长的初始化程序,主要是默认值。文档比我解释得更好,请查看!
这是一个简单的例子,取自Apple Developer portal。
基本上指定的初始化器是init(name: String)
,它确保所有存储的属性都被初始化。
init()
便利初始化器不带任何参数,使用指定的初始化器自动将 属性 存储的 name
的值设置为 [Unnamed]
。
class Food {
let name: String
// MARK: - designated initializer
init(name: String) {
self.name = name
}
// MARK: - convenience initializer
convenience init() {
self.init(name: "[Unnamed]")
}
}
// MARK: - Examples
let food = Food(name: "Cheese") // name will be "Cheese"
let food = Food() // name will be "[Unnamed]"
当您处理大型 类 时,它很有用,至少有一些存储属性。我建议在 Apple Developer portal.
阅读更多关于可选和继承的内容
注意:阅读全文
指定初始化程序是 class 的主要初始化程序。指定的初始化程序完全初始化由 class 引入的所有属性,并调用适当的 superclass 初始化程序以继续初始化过程直到 superclass 链。
便利初始化器是次要的,支持 class 的初始化器。您可以定义一个便利初始化器,从与便利初始化器相同的 class 调用指定初始化器,并将指定初始化器的一些参数设置为默认值。
classes 的指定初始化器的编写方式与值类型的简单初始化器相同:
init(parameters) {
statements
}
便利初始化器以相同的风格编写,但便利修饰符放在 init 关键字之前,由 space:
分隔
convenience init(parameters) {
statements
}
实际例子如下:
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
let namedMeat = Food(name: "Bacon")
// namedMeat's name is "Bacon”
Food class 中的 init(name: String) 初始化器作为指定初始化器提供,因为它确保新 Food 实例的所有存储属性都被完全初始化。 Food class 没有 superclass,因此 init(name: String) 初始化器不需要调用 super.init() 来完成它的初始化。
“The Food class 还提供了一个方便的初始化程序,init(),没有参数。 init() 初始化器通过委托给 Food class 的 init(name: String) 为新食物提供默认占位符名称,名称值为 [Unnamed]:”
“let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]”
层次结构中的第二个 class 是 Food 的子class,称为 RecipeIngredient。 RecipeIngredient class 模拟烹饪食谱中的成分。它引入了一个名为 quantity 的 Int 属性(除了它从 Food 继承的名称 属性 之外)并定义了两个用于创建 RecipeIngredient 实例的初始化器:
class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}
RecipeIngredient class 有一个指定的初始值设定项 init(name: String, quantity: Int),可用于填充新 RecipeIngredient 实例的所有属性。此初始化程序首先将传递的数量参数分配给数量 属性,这是 RecipeIngredient 引入的唯一新的 属性。这样做之后,初始化器委托给 Food class.
的 init(name: String) 初始化器
page:536 摘自:Apple Inc.“Swift 编程语言 (Swift 4)。”电子书。 https://itunes.apple.com/pk/book/the-swift-programming-language-swift-4-0-3/id881256329?mt=11
因此,当您不需要为 class 指定每个 属性 时,它会派上用场。因此,例如,如果我想创建起始 HP 值为 100 的所有冒险,我会在 convenience init 之后使用它并添加一个名称。这将大大减少代码。
class Adventure {
// Instance Properties
var name: String
var hp: Int
let maxHealth: Int = 100
// Optionals
var specialMove: String?
init(name: String, hp: Int) {
self.name = name
self.hp = hp
}
convenience init(name: String){
self.init(name: name, hp: 100)
}
}
便利初始化器击败设置默认参数值的地方
对我来说,如果除了简单地为 class 属性.
设置默认值之外,还有更多事情要做,convenience initializers
很有用
Class 指定 init()
的实现
否则,我会简单地在 init
定义中设置默认值,例如:
class Animal {
var race: String // enum might be better but I am using string for simplicity
var name: String
var legCount: Int
init(race: String = "Dog", name: String, legCount: Int = 4) {
self.race = race
self.name = name
self.legCount = legCount // will be 4 by default
}
}
Class 带有方便 init()
的扩展
然而,除了简单地设置默认值之外,还有更多事情要做,这就是 convenience initializers
派上用场的地方:
extension Animal {
convenience init(race: String, name: String) {
var legs: Int
if race == "Dog" {
legs = 4
} else if race == "Spider" {
legs = 8
} else {
fatalError("Race \(race) needs to be implemented!!")
}
// will initialize legCount automatically with correct number of legs if race is implemented
self.init(race: race, name: name, legCount: legs)
}
}
使用示例
// default init with all default values used
let myFirstDog = Animal(name: "Bello")
// convenience init for Spider as race
let mySpider = Animal(race: "Spider", name: "Itzy")
// default init with all parameters set by user
let myOctopus = Animal(race: "Octopus", name: "Octocat", legCount: 16)
// convenience init with Fatal error: Race AlienSpecies needs to be implemented!!
let myFault = Animal(race: "AlienSpecies", name: "HelloEarth")
可以在 class 扩展 中定义便捷初始化器。但是一个标准的 - 不能。
所有答案听起来都不错,但是,让我们通过一个简单的例子来理解它
class X{
var temp1
init(a: Int){
self.temp1 = a
}
现在,我们知道一个class可以继承另一个class,所以
class Z: X{
var temp2
init(a: Int, b: Int){
self.temp2 = b
super.init(a: a)
}
现在,在这种情况下,在为 class Z 创建实例时,您必须同时提供值 'a' 和 'b'。
let z = Z(a: 1, b: 2)
但是,如果您只想传递b 的值并希望rest 为其他值取默认值怎么办,那么在这种情况下您需要将其他值初始化为默认值。但是等等如何?,因为你只需要在 class 之前设置好它。
//This is inside the class Z, so consider it inside class Z's declaration
convenience init(b: Int){
self.init(a: 0, b: b)
}
convenience init(){
self.init(a: 0, b: 0)
}
现在,您可以通过为变量提供部分、全部或 none 值来创建 class Z 的实例。
let z1 = Z(b: 2)
let z2 = Z()
convenience init
可以选择用值初始化 class。
如果您的 用例 是在同一个 class 中的另一个初始化程序中调用 一个初始化程序,这是有道理的。 =13=]
尝试在 游乐场
class Player {
let name: String
let level: Int
init(name: String, level: Int) {
self.name = name
self.level = level
}
init(name: String) {
self.init(name: name, level: 0) //<- Call the initializer above?
//Sorry you can't do that. How about adding a convenience keyword?
}
}
Player(name:"LoseALot")
使用便捷关键字
class Player {
let name: String
let level: Int
init(name: String, level: Int) {
self.name = name
self.level = level
}
//Add the convenience keyword
convenience init(name: String) {
self.init(name: name, level: 0) //Yes! I am now allowed to call my fellow initializer!
}
}
如果您想通过扩展为某个类型创建自己的自定义初始值设定项,这也很有用。
class Player {
let name: String
let level: Int
init(name: String, level: Int) {
self.name = name
self.level = level
}
}
extension Player {
convenience init(name: String) {
self.init(name: name, level: 0)
}
}
我想为@Chris Graf 示例再添加 2 点 ---
Class 必须在声明或“init”方法中初始化所有存储的属性(不需要初始化可选值)。 Convenience inits 在 default init 之上工作,即,它在调用 default init 之前或 default init 之后添加额外的设置。
“便捷”初始化器的优点
1.Same“convenience init”实现的功能也可以用init(//—init 2 with only two properties)方法实现,但我们需要重新初始化所有属性,而不是使用默认初始化(//—具有所有属性的初始化 1)。原因是“init”方法不允许调用“self.init”,即使参数数量不同。因此,代码重复将在那里。
2.Additional 设置也可以在默认初始化程序中完成。但是如果你只想在某些场景中初始化存储属性,而在其他场景中进行额外的设置,那么你可能需要 2 个不同的初始化器。但是,如果如上所述声明了 2 个不同的 init,我们将结束在两个 init 中再次编写相同的代码。因此,当需要两个 init 时使用“便利”init,即带有额外设置的 init(便利 init)和仅带有存储属性初始化的 init。
注意:便利初始化 — 必须始终调用默认初始化。原因是通过调用 Convenience init,class 实例不会被初始化。所以在我们调用“self.init()”之前,“convenience init”不能访问“self”。
class Animal {
var race: String
var name: String
var legCount: Int
init(race: String = "Dog", name: String, legCount: Int = 4)
{//—init 1
`enter code here`with all properties
self.race = race
self.name = name
self.legCount = legCount // will be 4 by default
print("came to init 1")
}
init(race: String, name: String) {//—init 2 with only two properties)
var legs = 0
if race == "Dog" {
legs = 4
} else if race == "Spider" {
legs = 8
}
legCount = legs
self.race = race
self.name = name
print("came to init 2")
}
convenience init(race: String) {
print("came to convenience init")
var legs = 0
let name = "default"
if race == "Dog" {
legs = 4
} else if race == "Spider" {
legs = 8
}
else {
fatalError("Race \(race) needs to be implemented!!")
}
// will initialize legCount automatically with correct number of legs if race is implemented
self.init(race: race, name: name, legCount: legs)
}
}
// default init1 with all default values used
let myFirstDog = Animal(name: "Bello")
// default init1 with all parameters set by user
let myOctopus = Animal(race: "Octopus", name: "Octocat", legCount: 16)
// default init2 with 2 parameters
let myOctopus1 = Animal(race:"Octopus1" , name:"Octocat1")
// convenience init for Spider as race
let mySpider = Animal(race: "Spider")
我很难理解两者之间的区别,或者 convenience init
的目的。
当你有一些 class 有很多属性时使用便利初始化器,这使得它有点 "Painful" 总是用所有变量初始化机智,所以你用便利初始化器做的是您只需传递一些变量来初始化 object,并为其余变量分配默认值。 Ray Wenderlich 网站上有一个非常好的视频,不确定它是否免费,因为我有一个付费帐户。 这是一个示例,您可以看到我没有用所有这些变量初始化我的 object,而是给它一个标题。
struct Scene {
var minutes = 0
}
class Movie {
var title: String
var author: String
var date: Int
var scenes: [Scene]
init(title: String, author: String, date: Int) {
self.title = title
self.author = author
self.date = date
scenes = [Scene]()
}
convenience init(title:String) {
self.init(title:title, author: "Unknown", date:2016)
}
func addPage(page: Scene) {
scenes.append(page)
}
}
var myMovie = Movie(title: "my title") // Using convenicence initializer
var otherMovie = Movie(title: "My Title", author: "My Author", date: 12) // Using a long normal initializer
标准 init
:
Designated initializers are the primary initializers for a class. A designated initializer fully initializes all properties introduced by that class and calls an appropriate superclass initializer to continue the initialization process up the superclass chain.
convenience init
:
Convenience initializers are secondary, supporting initializers for a class. You can define a convenience initializer to call a designated initializer from the same class as the convenience initializer with some of the designated initializer’s parameters set to default values. You can also define a convenience initializer to create an instance of that class for a specific use case or input value type.
简而言之,这意味着您可以使用便利构造器来使调用指定构造器更快、更“方便”。因此,便利初始化器需要使用 self.init
而不是您可能在指定初始化器的覆盖中看到的 super.init
。
伪代码示例:
init(param1, param2, param3, ... , paramN) {
// code
}
// can call this initializer and only enter one parameter,
// set the rest as defaults
convenience init(myParamN) {
self.init(defaultParam1, defaultParam2, defaultParam3, ... , myParamN)
}
我在创建自定义视图时经常使用这些,这些视图有很长的初始化程序,主要是默认值。文档比我解释得更好,请查看!
这是一个简单的例子,取自Apple Developer portal。
基本上指定的初始化器是init(name: String)
,它确保所有存储的属性都被初始化。
init()
便利初始化器不带任何参数,使用指定的初始化器自动将 属性 存储的 name
的值设置为 [Unnamed]
。
class Food {
let name: String
// MARK: - designated initializer
init(name: String) {
self.name = name
}
// MARK: - convenience initializer
convenience init() {
self.init(name: "[Unnamed]")
}
}
// MARK: - Examples
let food = Food(name: "Cheese") // name will be "Cheese"
let food = Food() // name will be "[Unnamed]"
当您处理大型 类 时,它很有用,至少有一些存储属性。我建议在 Apple Developer portal.
阅读更多关于可选和继承的内容注意:阅读全文
指定初始化程序是 class 的主要初始化程序。指定的初始化程序完全初始化由 class 引入的所有属性,并调用适当的 superclass 初始化程序以继续初始化过程直到 superclass 链。
便利初始化器是次要的,支持 class 的初始化器。您可以定义一个便利初始化器,从与便利初始化器相同的 class 调用指定初始化器,并将指定初始化器的一些参数设置为默认值。
classes 的指定初始化器的编写方式与值类型的简单初始化器相同:
init(parameters) {
statements
}
便利初始化器以相同的风格编写,但便利修饰符放在 init 关键字之前,由 space:
分隔convenience init(parameters) {
statements
}
实际例子如下:
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
let namedMeat = Food(name: "Bacon")
// namedMeat's name is "Bacon”
Food class 中的 init(name: String) 初始化器作为指定初始化器提供,因为它确保新 Food 实例的所有存储属性都被完全初始化。 Food class 没有 superclass,因此 init(name: String) 初始化器不需要调用 super.init() 来完成它的初始化。
“The Food class 还提供了一个方便的初始化程序,init(),没有参数。 init() 初始化器通过委托给 Food class 的 init(name: String) 为新食物提供默认占位符名称,名称值为 [Unnamed]:”
“let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]”
层次结构中的第二个 class 是 Food 的子class,称为 RecipeIngredient。 RecipeIngredient class 模拟烹饪食谱中的成分。它引入了一个名为 quantity 的 Int 属性(除了它从 Food 继承的名称 属性 之外)并定义了两个用于创建 RecipeIngredient 实例的初始化器:
class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}
RecipeIngredient class 有一个指定的初始值设定项 init(name: String, quantity: Int),可用于填充新 RecipeIngredient 实例的所有属性。此初始化程序首先将传递的数量参数分配给数量 属性,这是 RecipeIngredient 引入的唯一新的 属性。这样做之后,初始化器委托给 Food class.
的 init(name: String) 初始化器page:536 摘自:Apple Inc.“Swift 编程语言 (Swift 4)。”电子书。 https://itunes.apple.com/pk/book/the-swift-programming-language-swift-4-0-3/id881256329?mt=11
因此,当您不需要为 class 指定每个 属性 时,它会派上用场。因此,例如,如果我想创建起始 HP 值为 100 的所有冒险,我会在 convenience init 之后使用它并添加一个名称。这将大大减少代码。
class Adventure {
// Instance Properties
var name: String
var hp: Int
let maxHealth: Int = 100
// Optionals
var specialMove: String?
init(name: String, hp: Int) {
self.name = name
self.hp = hp
}
convenience init(name: String){
self.init(name: name, hp: 100)
}
}
便利初始化器击败设置默认参数值的地方
对我来说,如果除了简单地为 class 属性.
设置默认值之外,还有更多事情要做,convenience initializers
很有用
Class 指定 init()
的实现否则,我会简单地在 init
定义中设置默认值,例如:
class Animal {
var race: String // enum might be better but I am using string for simplicity
var name: String
var legCount: Int
init(race: String = "Dog", name: String, legCount: Int = 4) {
self.race = race
self.name = name
self.legCount = legCount // will be 4 by default
}
}
Class 带有方便 init()
的扩展然而,除了简单地设置默认值之外,还有更多事情要做,这就是 convenience initializers
派上用场的地方:
extension Animal {
convenience init(race: String, name: String) {
var legs: Int
if race == "Dog" {
legs = 4
} else if race == "Spider" {
legs = 8
} else {
fatalError("Race \(race) needs to be implemented!!")
}
// will initialize legCount automatically with correct number of legs if race is implemented
self.init(race: race, name: name, legCount: legs)
}
}
使用示例
// default init with all default values used
let myFirstDog = Animal(name: "Bello")
// convenience init for Spider as race
let mySpider = Animal(race: "Spider", name: "Itzy")
// default init with all parameters set by user
let myOctopus = Animal(race: "Octopus", name: "Octocat", legCount: 16)
// convenience init with Fatal error: Race AlienSpecies needs to be implemented!!
let myFault = Animal(race: "AlienSpecies", name: "HelloEarth")
可以在 class 扩展 中定义便捷初始化器。但是一个标准的 - 不能。
所有答案听起来都不错,但是,让我们通过一个简单的例子来理解它
class X{
var temp1
init(a: Int){
self.temp1 = a
}
现在,我们知道一个class可以继承另一个class,所以
class Z: X{
var temp2
init(a: Int, b: Int){
self.temp2 = b
super.init(a: a)
}
现在,在这种情况下,在为 class Z 创建实例时,您必须同时提供值 'a' 和 'b'。
let z = Z(a: 1, b: 2)
但是,如果您只想传递b 的值并希望rest 为其他值取默认值怎么办,那么在这种情况下您需要将其他值初始化为默认值。但是等等如何?,因为你只需要在 class 之前设置好它。
//This is inside the class Z, so consider it inside class Z's declaration
convenience init(b: Int){
self.init(a: 0, b: b)
}
convenience init(){
self.init(a: 0, b: 0)
}
现在,您可以通过为变量提供部分、全部或 none 值来创建 class Z 的实例。
let z1 = Z(b: 2)
let z2 = Z()
convenience init
可以选择用值初始化 class。
如果您的 用例 是在同一个 class 中的另一个初始化程序中调用 一个初始化程序,这是有道理的。 =13=]
尝试在 游乐场 使用便捷关键字 如果您想通过扩展为某个类型创建自己的自定义初始值设定项,这也很有用。class Player {
let name: String
let level: Int
init(name: String, level: Int) {
self.name = name
self.level = level
}
init(name: String) {
self.init(name: name, level: 0) //<- Call the initializer above?
//Sorry you can't do that. How about adding a convenience keyword?
}
}
Player(name:"LoseALot")
class Player {
let name: String
let level: Int
init(name: String, level: Int) {
self.name = name
self.level = level
}
//Add the convenience keyword
convenience init(name: String) {
self.init(name: name, level: 0) //Yes! I am now allowed to call my fellow initializer!
}
}
class Player {
let name: String
let level: Int
init(name: String, level: Int) {
self.name = name
self.level = level
}
}
extension Player {
convenience init(name: String) {
self.init(name: name, level: 0)
}
}
我想为@Chris Graf 示例再添加 2 点 ---
Class 必须在声明或“init”方法中初始化所有存储的属性(不需要初始化可选值)。 Convenience inits 在 default init 之上工作,即,它在调用 default init 之前或 default init 之后添加额外的设置。
“便捷”初始化器的优点
1.Same“convenience init”实现的功能也可以用init(//—init 2 with only two properties)方法实现,但我们需要重新初始化所有属性,而不是使用默认初始化(//—具有所有属性的初始化 1)。原因是“init”方法不允许调用“self.init”,即使参数数量不同。因此,代码重复将在那里。
2.Additional 设置也可以在默认初始化程序中完成。但是如果你只想在某些场景中初始化存储属性,而在其他场景中进行额外的设置,那么你可能需要 2 个不同的初始化器。但是,如果如上所述声明了 2 个不同的 init,我们将结束在两个 init 中再次编写相同的代码。因此,当需要两个 init 时使用“便利”init,即带有额外设置的 init(便利 init)和仅带有存储属性初始化的 init。
注意:便利初始化 — 必须始终调用默认初始化。原因是通过调用 Convenience init,class 实例不会被初始化。所以在我们调用“self.init()”之前,“convenience init”不能访问“self”。
class Animal {
var race: String
var name: String
var legCount: Int
init(race: String = "Dog", name: String, legCount: Int = 4)
{//—init 1
`enter code here`with all properties
self.race = race
self.name = name
self.legCount = legCount // will be 4 by default
print("came to init 1")
}
init(race: String, name: String) {//—init 2 with only two properties)
var legs = 0
if race == "Dog" {
legs = 4
} else if race == "Spider" {
legs = 8
}
legCount = legs
self.race = race
self.name = name
print("came to init 2")
}
convenience init(race: String) {
print("came to convenience init")
var legs = 0
let name = "default"
if race == "Dog" {
legs = 4
} else if race == "Spider" {
legs = 8
}
else {
fatalError("Race \(race) needs to be implemented!!")
}
// will initialize legCount automatically with correct number of legs if race is implemented
self.init(race: race, name: name, legCount: legs)
}
}
// default init1 with all default values used
let myFirstDog = Animal(name: "Bello")
// default init1 with all parameters set by user
let myOctopus = Animal(race: "Octopus", name: "Octocat", legCount: 16)
// default init2 with 2 parameters
let myOctopus1 = Animal(race:"Octopus1" , name:"Octocat1")
// convenience init for Spider as race
let mySpider = Animal(race: "Spider")