我很难理解两者之间的区别,或者方便init的目的。 感谢
答案 0 :(得分:66)
标准init
:
指定的初始值设定项是类的主要初始值设定项。一个 指定的初始化程序完全初始化所引入的所有属性 该类并调用适当的超类初始化程序继续 初始化过程超级链。
convenience init
:
便利初始化程序是次要的,支持a的初始化程序 类。您可以定义一个便捷初始化程序来调用指定的 初始化程序来自同一类的便捷初始化程序 指定的初始化程序的参数设置为默认值。您可以 还定义了一个便利初始化器来创建该类的实例 对于特定用例或输入值类型。
基本上所有这一切都意味着你使用一个方便的初始化程序来更快地调用指定的初始化程序,并且更方便"。因此,便利初始化程序需要使用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)
}
我在创建自定义视图时使用了很多这样的视图,并且使用主要是默认值的长初始化程序。文档比我更好地解释,检查出来!
答案 1 :(得分:44)
当你有一些具有大量属性的类时会使用便利性初始化器,这使得总是用所有变量初始化机智会“痛苦”,所以你使用便利初始化器做的是你只是传递一些变量初始化对象,并为其余部分分配默认值。 Ray Wenderlich网站上有一个非常好的视频,不确定它是否免费,因为我有一个付费帐户。 这是一个例子,你可以看到,而不是用所有这些变量初始化我的对象我只是给它一个标题。
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
答案 2 :(得分:10)
以下是一个简单的示例,取自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.
上阅读更多关于选项和继承的内容答案 3 :(得分:2)
注意:阅读整篇文章
指定的初始值设定项是类的主要初始值设定项。指定的初始化程序完全初始化该类引入的所有属性,并调用适当的超类初始化程序以继续初始化过程直到超类链。
便利初始化程序是次要的,支持类的初始化程序。您可以定义一个便捷初始值设定项,以便从与初始值设定项相同的类中调用指定的初始值设定项,并将某些指定的初始化程序参数设置为默认值。
类的指定初始值设定项的编写方式与值类型的简单初始值设定项相同:
init(parameters) {
statements
}
便捷初始化程序以相同的样式编写,但是在init关键字之前放置了方便修饰符,用空格分隔:
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类中的init(name:String)初始化程序作为指定的初始值设定项提供,因为它可确保完全初始化新Food实例的所有存储属性。 Food类没有超类,因此init(name:String)初始值设定项不需要调用super.init()来完成初始化。
“Food类还提供了一个方便的初始化器,init(),没有参数。 init()初始值设定项通过委托名为[Unnamed]的食物类的init(name:String)来为新食物提供默认的占位符名称:“
“let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]”
层次结构中的第二个类是Food的子类,名为RecipeIngredient。 RecipeIngredient类为烹饪配方中的成分建模。它引入了一个名为quantity的Int属性(除了它继承自Food之外的name属性),并定义了两个用于创建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类有一个指定的初始化程序init(name:String,quantity:Int),可用于填充新RecipeIngredient实例的所有属性。此初始化程序首先将传递的数量参数分配给quantity属性,这是RecipeIngredient引入的唯一新属性。执行此操作后,初始化程序将委托给Food类的init(name:String)初始化程序。
页面:536摘录自:Apple Inc.“The Swift Programming Language(Swift 4)。”iBooks。 https://itunes.apple.com/pk/book/the-swift-programming-language-swift-4-0-3/id881256329?mt=11
答案 4 :(得分:2)
因此,当您不需要为类指定每个属性时,它会派上用场。因此,举例来说,如果我想创建所有HP起始值为100的冒险,我会在 community 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)
}
}
答案 5 :(得分:2)
答案 6 :(得分:1)
对我来说,convenience initializers
不仅可以为类属性设置默认值,还可以做很多事情。
否则,我只需在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
}
}
但是,除了简单地设置默认值之外,还有很多事情要做,这就是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")
答案 7 :(得分:1)
可以在类扩展中定义便捷初始化程序。但是一个标准的-不能。
答案 8 :(得分:1)
所有答案听起来都不错,但是让我们通过一个简单的例子来理解
class X{
var temp1
init(a: Int){
self.temp1 = a
}
现在,我们知道一个类可以继承另一个类,所以
class Z: X{
var temp2
init(a: Int, b: Int){
self.temp2 = b
super.init(a: a)
}
现在,在这种情况下,为类Z创建实例时,您将必须同时提供值“ a”和“ b”。
let z = Z(a: 1, b: 2)
但是,如果您只想传递b的值而希望其余的取其他值作为默认值,那么在这种情况下,您需要将其他值初始化为默认值。但是请等一下吗?,因为那个U只能在班级之前设置好。
//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)
}
现在,您可以通过提供变量的一些,全部或不提供值来创建Z类的实例。
let z1 = Z(b: 2)
let z2 = Z()
答案 9 :(得分:1)
如果您的用例是要在同一类的另一个初始值设定项中调用初始值设定项,则很有意义。
尝试在游乐场
中执行此操作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!
}
}