快速初始化和便捷实例之间的便利初始化和初始化之间有什么区别


Answers:


105

标准init

指定的初始化器是类的主要初始化器。指定的初始化程序将完全初始化该类引入的所有属性,并调用适当的超类初始化程序以继续超类链中的初始化过程。

convenience init

便利初始化器是辅助的,支持类的初始化器。您可以定义一个便捷初始化程序,以从与便捷初始化程序相同的类中调用一个指定初始化程序,并将某些指定初始值设定项的参数设置为默认值。您还可以定义一个便捷初始化程序,以针对特定用例或输入值类型创建该类的实例。

根据Swift文档

简而言之,这意味着您可以使用便捷的初始化程序来更快,更“方便”地调用指定的初始化程序。因此,便利的初始化程序需要使用,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)
}

在创建自定义视图时,我会经常使用它们,并且此类视图具有较长的初始化程序(主要是默认值)。文档比我做得更好,请解释一下!


73

当您有一些具有很多属性的类时,便会使用便捷初始化程序,这使得总是用所有这些变量初始化机智是“痛苦的”,因此使用便捷初始化程序的工作是只传递一些变量以初始化对象,并为其余的对象分配默认值。雷·温德利奇(Ray Wenderlich)网站上有一个非常不错的视频,不确定我是否有免费视频,因为我有一个付费帐户。这是一个示例,您可以看到它没有使用所有这些变量Im初始化我的对象,而只是给了它一个标题。

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

4
好的例子。这使概念很明确。谢谢。
Arjun Kalidas '18

5
我们还可以使用具有默认值author和日期参数的init()作为`init(title:String,author:String =“ Unknown”,date:Int = 2016){self.title =标题self.author =作者self。日期=日期场景= [Scene]()}`那么为什么我们需要方便的初始化方法?
shripad20

@ shripad20同样的问题在这里。您是否找到了为什么我们必须使用便利性的原因,尽管我们可以创建另一个初始化器并从中发送默认参数。如果您找到答案,请告诉我
user100

方便是“ main” self.init的附加功能,而具有默认参数的self.init替代了“ main” self.init。 hackingwithswift.com/example-code/language/…–
user1105951

@ shripad20请在下面的示例中查看有关澄清的信息,其中便利初始化程序击败了默认值。希望能帮助到你。
克里斯·格拉夫

14

这是一个简单的示例,摘自Apple Developer门户

基本上,指定的初始值设定项是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门户网站上阅读有关可选和继承的更多信息


6

便利初始化程序优于设置默认参数值的地方

对我来说,convenience initializers如果要做的事情不仅仅是为类属性设置默认值,那么它很有用。

具有指定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
    }
}

具有方便的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")

1
好又简单的解释在这里
vivi

4

可以在类扩展中定义便捷初始化程序。但是一个标准的-不能。


1
尽管还不能完全回答这个问题,但这是我尚未考虑的问题,谢谢!
马克·安德烈·韦贝赞

3

convenience init使得初始化带有值的类成为可选项。

在此处输入图片说明

在此处输入图片说明


3

如果您的用例在同一类的另一个初始值设定项中调用初始值设定项,则是有意义的。

尝试在操场上这样做

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!
    }
}

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()初始化程序通过委派给Food类的init(名称:String),并使用名称值[Unnamed]来为新食物提供默认的占位符名称:”

let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]”

层次结构中的第二个类是Food的子类,名为RecipeIngredient。RecipeIngredient类对烹饪食谱中的成分进行建模。它引入了一个称为数量的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实例的所有属性。该初始化程序首先将传递的数量参数分配给数量属性,这是RecipeIngredient引入的唯一新属性。这样做之后,初始化器将委托给Food类的init(name:String)初始化器。

第536页摘录自:Apple Inc.“ Swift编程语言(Swift 4)”。iBooks。https://itunes.apple.com/pk/book/the-swift-programming-language-swift-4-0-3/id881256329?mt=11


2

因此,当您不需要为类指定每个属性时,它会派上用场。因此,例如,如果我想创建所有起始HP值为100的冒险,我将使用以下便捷初始化并仅添加一个名称。这将大大减少代码。

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)
    }
}

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()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.