Initialization (构造过程上)

作者: 金旭峰 | 来源:发表于2017-01-19 11:02 被阅读35次

    Initializationis the process of preparing an instance of a class, structure, or enumeration for use. This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization that is required before the new instance is ready for use.

    构造过程是使用类、结构体或枚举类型的实例之前的准备过程。在新实例可用前必须执行这个过程,具体操作包括设置实例中每个存储型属性的初始值和执行其他必须的设置或初始化工作。

    You implement this initialization process by defininginitializers, which are like special methods that can be called to create a new instance of a particular type. Unlike Objective-C initializers, Swift initializers do not return a value. Their primary role is to ensure that new instances of a type are correctly initialized before they are used for the first time.

    通过定义构造器来实现构造过程,这些构造器可以看做是用来创建特定类型新实例的特殊方法。与 Objective-C 中的构造器不同,Swift 的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化。

    Instances of class types can also implement adeinitializer, which performs any custom cleanup just before an instance of that class is deallocated. For more information about deinitializers, seeDeinitialization.

    类的实例也可以通过定义析构器在实例释放之前执行特定的清除工作。想了解更多关于析构器的内容,请参考析构过程

    Setting Initial Values for Stored Properties (为存储属性设置初始值)

    Classes and structuresmustset all of their stored properties to an appropriate initial value by the time an instance of that class or structure is created. Stored properties cannot be left in an indeterminate state.

    类和结构在创建该类或结构的实例时将所有存储的属性设置为适当的初始值。 存储的属性不能保留在不确定状态。

    You can set an initial value for a stored property within an initializer, or by assigning a default property value as part of the property’s definition. These actions are described in the following sections.

    您可以为初始化程序中的存储属性设置初始值,或者通过将默认属性值指定为属性定义的一部分。 这些操作将在以下部分中描述。

    NOTE

    When you assign a default value to a stored property, or set its initial value within an initializer, the value of that property is set directly, without calling any property observers.

    当为存储属性分配默认值或在初始化器中设置其初始值时,将直接设置该属性的值,而不调用任何属性观察器。

    Initializers (构造器)

    Initializersare called to create a new instance of a particular type. In its simplest form, an initializer is like an instance method with no parameters, written using theinitkeyword:

    构造器在创建某个特定类型的新实例时被调用。它的最简形式类似于一个不带任何参数的实例方法,以关键字init命名:

    init() {

        // perform some initialization here

    }

    The example below defines a new structure calledFahrenheitto store temperatures expressed in the Fahrenheit scale. TheFahrenheitstructure has one stored property,temperature, which is of typeDouble:

    下面例子中定义了一个用来保存华氏温度的结构体Fahrenheit,它拥有一个Double类型的存储型属性temperature:

    struct Fahrenheit {

        var temperature:Double

        init() {

            temperature=32.0

        }

    }

    var f = Fahrenheit()

    print("The default temperature is\(f.temperature)° Fahrenheit")

    // Prints "The default temperature is 32.0° Fahrenheit"

    The structure defines a single initializer,init, with no parameters, which initializes the stored temperature with a value of32.0(the freezing point of water in degrees Fahrenheit).

    这个结构体定义了一个不带参数的构造器init,并在里面将存储型属性temperature的值初始化为32.0(华氏温度下水的冰点)。

    Default Property Values (默认属性值)

    You can set the initial value of a stored property from within an initializer, as shown above. Alternatively, specify adefault property valueas part of the property’s declaration. You specify a default property value by assigning an initial value to the property when it is defined.

    如前所述,你可以在构造器中为存储型属性设置初始值。同样,你也可以在属性声明时为其设置默认值。

    NOTE

    If a property always takes the same initial value, provide a default value rather than setting a value within an initializer. The end result is the same, but the default value ties the property’s initialization more closely to its declaration. It makes for shorter, clearer initializers and enables you to infer the type of the property from its default value. The default value also makes it easier for you to take advantage of default initializers and initializer inheritance, as described later in this chapter.

    如果一个属性总是使用相同的初始值,那么为其设置一个默认值比每次都在构造器中赋值要好。两种方法的效果是一样的,只不过使用默认值让属性的初始化和声明结合得更紧密。使用默认值能让你的构造器更简洁、更清晰,且能通过默认值自动推导出属性的类型;同时,它也能让你充分利用默认构造器、构造器继承等特性,后续章节将讲到。

    You can write theFahrenheitstructure from above in a simpler form by providing a default value for itstemperatureproperty at the point that the property is declared:

    你可以使用更简单的方式在定义结构体Fahrenheit时为属性temperature设置默认值:

    struct Fahrenheit {

        var temperature=32.0

    }

    Customizing Initialization (自定义构造函数)

    You can customize the initialization process with input parameters and optional property types, or by assigning constant properties during initialization, as described in the following sections.

    你可以通过输入参数和可选类型的属性来自定义构造过程,也可以在构造过程中修改常量属性。这些都将在后面章节中提到。

    Initialization Parameters (构造参数)

    You can provideinitialization parametersas part of an initializer’s definition, to define the types and names of values that customize the initialization process. Initialization parameters have the same capabilities and syntax as function and method parameters.

    自定义构造过程时,可以在定义中提供构造参数,指定所需值的类型和名字。构造参数的功能和语法跟函数和方法的参数相同。

    The following example defines a structure calledCelsius, which stores temperatures expressed in degrees Celsius. TheCelsiusstructure implements two custom initializers calledinit(fromFahrenheit:)andinit(fromKelvin:), which initialize a new instance of the structure with a value from a different temperature scale:

    下面例子中定义了一个包含摄氏度温度的结构体Celsius。它定义了两个不同的构造器:init(fromFahrenheit:)和init(fromKelvin:),二者分别通过接受不同温标下的温度值来创建新的实例:

    struct Celsius{

        var temperatureInCelsius:Double

        init (fromFahrenheitfahrenheit:Double) {

            temperatureInCelsius= (fahrenheit-32.0) /1.8

        }

        init (fromKelvinkelvin:Double) {

            temperatureInCelsius=kelvin-273.15

        }  

    }

    let boilingPointOfWater=Celsius(fromFahrenheit:212.0)

    // boilingPointOfWater.temperatureInCelsius is 100.0

    let freezingPointOfWater=Celsius(fromKelvin:273.15)

    // freezingPointOfWater.temperatureInCelsius is 0.0

    The first initializer has a single initialization parameter with an argument label offromFahrenheitand a parameter name offahrenheit. The second initializer has a single initialization parameter with an argument label offromKelvinand a parameter name ofkelvin. Both initializers convert their single argument into the corresponding Celsius value and store this value in a property calledtemperatureInCelsius.

    第一个构造器拥有一个构造参数,其外部名字为fromFahrenheit,内部名字为fahrenheit;第二个构造器也拥有一个构造参数,其外部名字为fromKelvin,内部名字为kelvin。这两个构造器都将唯一的参数值转换成摄氏温度值,并保存在属性temperatureInCelsius中。

    Parameter Names and Argument Labels (参数的内部名称和外部名称)

    As with function and method parameters, initialization parameters can have both a parameter name for use within the initializer’s body and an argument label for use when calling the initializer.

    跟函数和方法参数相同,构造参数也拥有一个在构造器内部使用的参数名字和一个在调用构造器时使用的外部参数名字。

    However, initializers do not have an identifying function name before their parentheses in the way that functions and methods do. Therefore, the names and types of an initializer’s parameters play a particularly important role in identifying which initializer should be called. Because of this, Swift provides an automatic argument label foreveryparameter in an initializer if you don’t provide one.

    然而,构造器并不像函数和方法那样在括号前有一个可辨别的名字。因此在调用构造器时,主要通过构造器中的参数名和类型来确定应该被调用的构造器。正因为参数如此重要,如果你在定义构造器时没有提供参数的外部名字,Swift 会为构造器的每个参数自动生成一个跟内部名字相同的外部名。

    The following example defines a structure calledColor, with three constant properties calledred,green, andblue. These properties store a value between0.0and1.0to indicate the amount of red, green, and blue in the color.

    以下例子中定义了一个结构体Color,它包含了三个常量:red、green和blue。这些属性可以存储0.0到1.0之间的值,用来指示颜色中红、绿、蓝成分的含量。

    Colorprovides an initializer with three appropriately named parameters of typeDoublefor its red, green, and blue components.Coloralso provides a second initializer with a singlewhiteparameter, which is used to provide the same value for all three color components.

    Color提供了一个构造器,其中包含三个Double类型的构造参数。Color也可以提供第二个构造器,它只包含名为white的Double类型的参数,它被用于给上述三个构造参数赋予同样的值。

    struct Color {

        let red,green,blue:Double

        init(red:Double,green:Double,blue:Double) {

            self.red=red

            self.green=green

            self.blue=blue

        }

        init (white:Double) {

            red=white

            green=white

            blue=white

        }

    }

    Both initializers can be used to create a newColorinstance, by providing named values for each initializer parameter:

    两种构造器都能用于创建一个新的Color实例,你需要为构造器每个外部参数传值:

    let magenta=Color(red:1.0,green:0.0,blue:1.0)

    let halfGray=Color(white:0.5)

    Note that it is not possible to call these initializers without using argument labels. Argument labels must always be used in an initializer if they are defined, and omitting them is a compile-time error:

    注意,如果不通过外部参数名字传值,你是没法调用这个构造器的。只要构造器定义了某个外部参数名,你就必须使用它,忽略它将导致编译错误:

    let veryGreen=Color(0.0,1.0,0.0)

    // this reports a compile-time error - argument labels are required

    Initializer Parameters Without Argument Labels (不带外部名的构造器参数)

    If you do not want to use an argument label for an initializer parameter, write an underscore (_) instead of an explicit argument label for that parameter to override the default behavior.

    如果你不希望为构造器的某个参数提供外部名字,你可以使用下划线(_)来显式描述它的外部名,以此重写上面所说的默认行为。

    Here’s an expanded version of theCelsiusexample from earlier, with an additional initializer to create a newCelsiusinstance from aDoublevalue that is already in the Celsius scale:

    下面是之前Celsius例子的扩展,跟之前相比添加了一个带有Double类型参数的构造器,其外部名用_代替:

    struct Celsius{

        var temperatureInCelsius:Double

        init (fromFahrenheitfahrenheit:Double) {

            temperatureInCelsius= (fahrenheit-32.0) /1.8

        }

        init (fromKelvinkelvin:Double) {

            temperatureInCelsius=kelvin-273.15

        }  

        init (_celsius:Double) {

            temperatureInCelsius=celsius

        }

    }

    let bodyTemperature=Celsius(37.0)

    // bodyTemperature.temperatureInCelsius is 37.0

    The initializer callCelsius(37.0)is clear in its intent without the need for an argument label. It is therefore appropriate to write this initializer asinit(_ celsius: Double)so that it can be called by providing an unnamedDoublevalue.

    调用Celsius(37.0)意图明确,不需要外部参数名称。因此适合使用init(_ celsius: Double)这样的构造器,从而可以通过提供Double类型的参数值调用构造器,而不需要加上外部名。

    Optional Property Types (可选属性类型)

    If your custom type has a stored property that is logically allowed to have “no value”—perhaps because its value cannot be set during initialization, or because it is allowed to have “no value” at some later point—declare the property with anoptionaltype. Properties of optional type are automatically initialized with a value ofnil, indicating that the property is deliberately intended to have “no value yet” during initialization.

    如果你定制的类型包含一个逻辑上允许取值为空的存储型属性——无论是因为它无法在初始化时赋值,还是因为它在之后某个时间点可以赋值为空——你都需要将它定义为可选类型。可选类型的属性将自动初始化为nil,表示这个属性是有意在初始化时设置为空的。

    The following example defines a class calledSurveyQuestion, with an optionalStringproperty calledresponse:

    下面例子中定义了类SurveyQuestion,它包含一个可选字符串属性response:

    class SurveyQuestion{

        var text:String

        var response:String?

        init (text:String) {

            self.text=text

        }

        func ask() {

        print(text)

        }

    }

    let cheeseQuestion=SurveyQuestion(text:"Do you like cheese?")

    cheeseQuestion.ask()

    // Prints "Do you like cheese?"

    cheeseQuestion.response="Yes, I do like cheese."

    The response to a survey question cannot be known until it is asked, and so theresponseproperty is declared with a type ofString?, or “optionalString”. It is automatically assigned a default value ofnil, meaning “no string yet”, when a new instance ofSurveyQuestionis initialized.

    调查问题的答案在回答前是无法确定的,因此我们将属性response声明为String?类型,或者说是可选字符串类型。当SurveyQuestion实例化时,它将自动赋值为nil,表明此字符串暂时还没有值。

    Assigning Constant Properties During Initialization (构造过程中常量属性的修改)

    You can assign a value to a constant property at any point during initialization, as long as it is set to a definite value by the time initialization finishes. Once a constant property is assigned a value, it can’t be further modified.

    你可以在构造过程中的任意时间点给常量属性指定一个值,只要在构造过程结束时是一个确定的值。一旦常量属性被赋值,它将永远不可更改。

    NOTE

    For class instances, a constant property can be modified during initialization only by the class that introduces it. It cannot be modified by a subclass.

    对于类的实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。

    You can revise theSurveyQuestionexample from above to use a constant property rather than a variable property for thetextproperty of the question, to indicate that the question does not change once an instance ofSurveyQuestionis created. Even though thetextproperty is now a constant, it can still be set within the class’s initializer:

    你可以修改上面的SurveyQuestion示例,用常量属性替代变量属性text,表示问题内容text在SurveyQuestion的实例被创建之后不会再被修改。尽管text属性现在是常量,我们仍然可以在类的构造器中设置它的值:

    class SurveyQuestion {

        let text:String

        var response:String?

        init (text:String) {  

            self.text=text

        }

        func ask() {

            print(text)

        }

    }

    let beetsQuestion=SurveyQuestion(text:"How about beets?")

    beetsQuestion.ask()

    // Prints "How about beets?"

    beetsQuestion.response="I also like beets. (But not with cheese.)"

    Default Initializers (默认构造器)

    Swift provides adefault initializerfor any structure or class that provides default values for all of its properties and does not provide at least one initializer itself. The default initializer simply creates a new instance with all of its properties set to their default values.

    如果结构体或类的所有属性都有默认值,同时没有自定义的构造器,那么 Swift 会给这些结构体或类提供一个默认构造器(default initializers)。这个默认构造器将简单地创建一个所有属性值都设置为默认值的实例。

    This example defines a class calledShoppingListItem, which encapsulates the name, quantity, and purchase state of an item in a shopping list:

    下面例子中创建了一个类ShoppingListItem,它封装了购物清单中的某一物品的属性:名字(name)、数量(quantity)和购买状态purchase state:

    class ShoppingListItem{

        var name:String?

        var quantity=1

        var purchased=false

    }

    var item=ShoppingListItem()

    Because all properties of theShoppingListItemclass have default values, and because it is a base class with no superclass,ShoppingListItemautomatically gains a default initializer implementation that creates a new instance with all of its properties set to their default values. (Thenameproperty is an optionalStringproperty, and so it automatically receives a default value ofnil, even though this value is not written in the code.) The example above uses the default initializer for theShoppingListItemclass to create a new instance of the class with initializer syntax, written asShoppingListItem(), and assigns this new instance to a variable calleditem.

    由于ShoppingListItem类中的所有属性都有默认值,且它是没有父类的基类,它将自动获得一个可以为所有属性设置默认值的默认构造器(尽管代码中没有显式为name属性设置默认值,但由于name是可选字符串类型,它将默认设置为nil)。上面例子中使用默认构造器创造了一个ShoppingListItem类的实例(使用ShoppingListItem()形式的构造器语法),并将其赋值给变量item。

    Memberwise Initializers for Structure Types (结构体的逐一成员构造器)

    Structure types automatically receive amemberwise initializerif they do not define any of their own custom initializers. Unlike a default initializer, the structure receives a memberwise initializer even if it has stored properties that do not have default values.

    除了上面提到的默认构造器,如果结构体没有提供自定义的构造器,它们将自动获得一个逐一成员构造器,即使结构体的存储型属性没有默认值。

    The memberwise initializer is a shorthand way to initialize the member properties of new structure instances. Initial values for the properties of the new instance can be passed to the memberwise initializer by name.

    逐一成员构造器是用来初始化结构体新实例里成员属性的快捷方法。我们在调用逐一成员构造器时,通过与成员属性名相同的参数名进行传值来完成对成员属性的初始赋值。

    The example below defines a structure calledSizewith two properties calledwidthandheight. Both properties are inferred to be of typeDoubleby assigning a default value of0.0.

    下面例子中定义了一个结构体Size,它包含两个属性width和height。Swift 可以根据这两个属性的初始赋值0.0自动推导出它们的类型为Double。

    TheSizestructure automatically receives aninit(width:height:)memberwise initializer, which you can use to initialize a newSizeinstance:

    结构体Size自动获得了一个逐一成员构造器init(width:height:)。你可以用它来为Size创建新的实例:

    struct Size{

        var width=0.0,height=0.0

    }

    let twoByTwo=Size(width:2.0,height:2.0)

    Initializer Delegation for Value Types (值类型的构造器代理)

    Initializers can call other initializers to perform part of an instance’s initialization. This process, known asinitializer delegation, avoids duplicating code across multiple initializers.

    构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能减少多个构造器间的代码重复。

    The rules for how initializer delegation works, and for what forms of delegation are allowed, are different for value types and class types. Value types (structures and enumerations) do not support inheritance, and so their initializer delegation process is relatively simple, because they can only delegate to another initializer that they provide themselves. Classes, however, can inherit from other classes, as described inInheritance. This means that classes have additional responsibilities for ensuring that all stored properties they inherit are assigned a suitable value during initialization. These responsibilities are described inClass Inheritance and Initializationbelow.

    构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给自己的其它构造器。类则不同,它可以继承自其它类(请参考继承),这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。这些责任将在后续章节类的继承和构造过程中介绍。

    For value types, you useself.initto refer to other initializers from the same value type when writing your own custom initializers. You can callself.initonly from within an initializer.

    对于值类型,你可以使用self.init在自定义的构造器中引用相同类型中的其它构造器。并且你只能在构造器内部调用self.init。

    Note that if you define a custom initializer for a value type, you will no longer have access to the default initializer (or the memberwise initializer, if it is a structure) for that type. This constraint prevents a situation in which additional essential setup provided in a more complex initializer is accidentally circumvented by someone using one of the automatic initializers.

    如果你为某个值类型定义了一个自定义的构造器,你将无法访问到默认构造器(如果是结构体,还将无法访问逐一成员构造器)。这种限制可以防止你为值类型增加了一个额外的且十分复杂的构造器之后,仍然有人错误的使用自动生成的构造器.

    NOTE

    If you want your custom value type to be initializable with the default initializer and memberwise initializer, and also with your own custom initializers, write your custom initializers in an extension rather than as part of the value type’s original implementation. For more information, seeExtensions.

    假如你希望默认构造器、逐一成员构造器以及你自己的自定义构造器都能用来创建实例,可以将自定义的构造器写到扩展(extension)中,而不是写在值类型的原始定义中。想查看更多内容,请查看扩展章节。

    The following example defines a customRectstructure to represent a geometric rectangle. The example requires two supporting structures calledSizeandPoint, both of which provide default values of0.0for all of their properties:

    下面例子将定义一个结构体Rect,用来代表几何矩形。这个例子需要两个辅助的结构体Size和Point,它们各自为其所有的属性提供了初始值0.0。

    struct Size{

        var width=0.0,height=0.0

    }

    struct Point{

     var x=0.0,y=0.0

    }

    You can initialize theRectstructure below in one of three ways—by using its default zero-initializedoriginandsizeproperty values, by providing a specific origin point and size, or by providing a specific center point and size. These initialization options are represented by three custom initializers that are part of theRectstructure’s definition:

    你可以通过以下三种方式为Rect创建实例——使用被初始化为默认值的origin和size属性来初始化;提供指定的origin和size实例来初始化;提供指定的center和size来初始化。在下面Rect结构体定义中,我们为这三种方式提供了三个自定义的构造器:

    struct Rect{

        var origin=Point()

        var size=Size()

        init () {}

        init (origin:Point,size:Size) {

            self.origin=origin

            self.size=size

        }

        init (center:Point,size:Size) {

            let originX=center.x- (size.width/2)

            let originY=center.y- (size.height/2)

            self.init(origin:Point(x:originX,y:originY),size:size)

        }

    }

    The firstRectinitializer,init(), is functionally the same as the default initializer that the structure would have received if it did not have its own custom initializers. This initializer has an empty body, represented by an empty pair of curly braces{}. Calling this initializer returns aRectinstance whoseoriginandsizeproperties are both initialized with the default values ofPoint(x: 0.0, y: 0.0)andSize(width: 0.0, height: 0.0)from their property definitions:

    第一个Rect构造器init(),在功能上跟没有自定义构造器时自动获得的默认构造器是一样的。这个构造器是一个空函数,使用一对大括号{}来表示,它没有执行任何构造过程。调用这个构造器将返回一个Rect实例,它的origin和size属性都使用定义时的默认值Point(x: 0.0, y: 0.0)和Size(width: 0.0, height: 0.0):

    let basicRect=Rect()

    // basicRect's origin is (0.0, 0.0) and its size is (0.0, 0.0)

    The secondRectinitializer,init(origin:size:), is functionally the same as the memberwise initializer that the structure would have received if it did not have its own custom initializers. This initializer simply assigns theoriginandsizeargument values to the appropriate stored properties:

    第二个Rect构造器init(origin:size:),在功能上跟结构体在没有自定义构造器时获得的逐一成员构造器是一样的。这个构造器只是简单地将origin和size的参数值赋给对应的存储型属性:

    let originRect=Rect(origin:Point(x:2.0,y:2.0),

    size:Size(width:5.0,height:5.0))

    // originRect's origin is (2.0, 2.0) and its size is (5.0, 5.0)

    The thirdRectinitializer,init(center:size:), is slightly more complex. It starts by calculating an appropriate origin point based on acenterpoint and asizevalue. It then calls (ordelegates) to theinit(origin:size:)initializer, which stores the new origin and size values in the appropriate properties:

    第三个Rect构造器init(center:size:)稍微复杂一点。它先通过center和size的值计算出origin的坐标,然后再调用(或者说代理给)init(origin:size:)构造器来将新的origin和size值赋值到对应的属性中:

    let centerRect=Rect(center:Point(x:4.0,y:4.0),

    size:Size(width:3.0,height:3.0))

    // centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)

    Theinit(center:size:)initializer could have assigned the new values oforiginandsizeto the appropriate properties itself. However, it is more convenient (and clearer in intent) for theinit(center:size:)initializer to take advantage of an existing initializer that already provides exactly that functionality.

    构造器init(center:size:)可以直接将origin和size的新值赋值到对应的属性中。然而,利用恰好提供了相关功能的现有构造器会更为方便,构造器init(center:size:)的意图也会更加清晰。

    NOTE

    For an alternative way to write this example without defining theinit()andinit(origin:size:)initializers yourself, seeExtensions.

    如果你想用另外一种不需要自己定义init()和init(origin:size:)的方式来实现这个例子,请参考扩展

    Class Inheritance and Initialization (类的继承和构造过程)

    All of a class’s stored properties—including any properties the class inherits from its superclass—mustbe assigned an initial value during initialization.

    类里面的所有存储型属性——包括所有继承自父类的属性——都必须在构造过程中设置初始值。

    Swift defines two kinds of initializers for class types to help ensure all stored properties receive an initial value. These are known as designated initializers and convenience initializers.

    Swift 为类类型提供了两种构造器来确保实例中所有存储型属性都能获得初始值,它们分别是指定构造器和便利构造器。

    Designated Initializers and Convenience Initializers (指定构造器和便利构造器)

    Designated initializersare 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.

    指定构造器是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。

    Classes tend to have very few designated initializers, and it is quite common for a class to have only one. Designated initializers are “funnel” points through which initialization takes place, and through which the initialization process continues up the superclass chain.

    类往往只有很少的指定的初始化器,并且一个类只有一个,这是相当普遍的。 指定的初始化器是通过其进行初始化的“漏斗”点,并且通过其初始化过程在超类链上继续。

    Every class must have at least one designated initializer. In some cases, this requirement is satisfied by inheriting one or more designated initializers from a superclass, as described inAutomatic Initializer Inheritancebelow.

    每一个类都必须拥有至少一个指定构造器。在某些情况下,许多类通过继承了父类中的指定构造器而满足了这个条件。具体内容请参考后续章节构造器的自动继承

    Convenience initializersare 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.

    便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入值的实例。

    You do not have to provide convenience initializers if your class does not require them. Create convenience initializers whenever a shortcut to a common initialization pattern will save time or make initialization of the class clearer in intent.

    你应当只在必要的时候为类提供便利构造器,比方说某种情况下通过使用便利构造器来快捷调用某个指定构造器,能够节省更多开发时间并让类的构造过程更清晰明了。

    Syntax for Designated and Convenience Initializers (指定构造器和便利构造器的语法)

    Designated initializers for classes are written in the same way as simple initializers for value types:

    类的指定构造器的写法跟值类型简单构造器一样:

    init (parameters) {

        statements

    }

    Convenience initializers are written in the same style, but with theconveniencemodifier placed before theinitkeyword, separated by a space:

    便利构造器也采用相同样式的写法,但需要在init关键字之前放置convenience关键字,并使用空格将它们俩分开:

    convenience init (parameters) {

        statements

    }

    Initializer Delegation for Class Types (类的构造器代理规则)

    To simplify the relationships between designated and convenience initializers, Swift applies the following three rules for delegation calls between initializers:

    为了简化指定构造器和便利构造器之间的调用关系,Swift 采用以下三条规则来限制构造器之间的代理调用:

    Rule 1 (规则一)

    A designated initializer must call a designated initializer from its immediate superclass.

    指定构造器必须调用其直接父类的的指定构造器。

    Rule 2 (规则一)

    A convenience initializer must call another initializer from thesameclass.

    便利构造器必须调用类中定义的其它构造器。

    Rule 3 (规则一)

    A convenience initializer must ultimately call a designated initializer.

    便利构造器必须最终导致一个指定构造器被调用。

    A simple way to remember this is:

    一个更方便记忆的方法是:

    1. Designated initializers must always delegateup.

    指定构造器必须总是向上代理

    2. Convenience initializers must always delegateacross.

    便利构造器必须总是横向代理

    These rules are illustrated in the figure below:

    这些规则可以通过下面图例来说明:

    Here, the superclass has a single designated initializer and two convenience initializers. One convenience initializer calls another convenience initializer, which in turn calls the single designated initializer. This satisfies rules 2 and 3 from above. The superclass does not itself have a further superclass, and so rule 1 does not apply.

    如图所示,父类中包含一个指定构造器和两个便利构造器。其中一个便利构造器调用了另外一个便利构造器,而后者又调用了唯一的指定构造器。这满足了上面提到的规则 2 和 3。这个父类没有自己的父类,所以规则 1 没有用到。

    The subclass in this figure has two designated initializers and one convenience initializer. The convenience initializer must call one of the two designated initializers, because it can only call another initializer from the same class. This satisfies rules 2 and 3 from above. Both designated initializers must call the single designated initializer from the superclass, to satisfy rule 1 from above.

    子类中包含两个指定构造器和一个便利构造器。便利构造器必须调用两个指定构造器中的任意一个,因为它只能调用同一个类里的其他构造器。这满足了上面提到的规则 2 和 3。而两个指定构造器必须调用父类中唯一的指定构造器,这满足了规则 1。

    NOTE

    These rules don’t affect how users of your classescreateinstances of each class. Any initializer in the diagram above can be used to create a fully-initialized instance of the class they belong to. The rules only affect how you write the implementation of the class’s initializers.

    这些规则不会影响类的实例如何创建。任何上图中展示的构造器都可以用来创建完全初始化的实例。这些规则只影响类定义如何实现。

    The figure below shows a more complex class hierarchy for four classes. It illustrates how the designated initializers in this hierarchy act as “funnel” points for class initialization, simplifying the interrelationships among classes in the chain:

    下面图例中展示了一种涉及四个类的更复杂的类层级结构。它演示了指定构造器是如何在类层级中充当“管道”的作用,在类的构造器链上简化了类之间的相互关系。

    Two-Phase Initialization (两段式构造过程)

    Class initialization in Swift is a two-phase process. In the first phase, each stored property is assigned an initial value by the class that introduced it. Once the initial state for every stored property has been determined, the second phase begins, and each class is given the opportunity to customize its stored properties further before the new instance is considered ready for use.

    Swift 中类的构造过程包含两个阶段。第一个阶段,每个存储型属性被引入它们的类指定一个初始值。当每个存储型属性的初始值被确定后,第二阶段开始,它给每个类一次机会,在新实例准备使用之前进一步定制它们的存储型属性。

    The use of a two-phase initialization process makes initialization safe, while still giving complete flexibility to each class in a class hierarchy. Two-phase initialization prevents property values from being accessed before they are initialized, and prevents property values from being set to a different value by another initializer unexpectedly.

    两段式构造过程的使用让构造过程更安全,同时在整个类层级结构中给予了每个类完全的灵活性。两段式构造过程可以防止属性值在初始化之前被访问,也可以防止属性被另外一个构造器意外地赋予不同的值。

    NOTE

    Swift’s two-phase initialization process is similar to initialization in Objective-C. The main difference is that during phase 1, Objective-C assigns zero or null values (such as0ornil) to every property. Swift’s initialization flow is more flexible in that it lets you set custom initial values, and can cope with types for which0ornilis not a valid default value.

    Swift 的两段式构造过程跟 Objective-C 中的构造过程类似。最主要的区别在于阶段 1,Objective-C 给每一个属性赋值0或空值(比如说0或nil)。Swift 的构造流程则更加灵活,它允许你设置定制的初始值,并自如应对某些属性不能以0或nil作为合法默认值的情况。

    Swift’s compiler performs four helpful safety-checks to make sure that two-phase initialization is completed without error:

    Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程能不出错地完成:

    Safety check 1 (安全检查一)

    A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.

    指定构造器必须保证它所在类引入的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中的构造器。

    As mentioned above, the memory for an object is only considered fully initialized once the initial state of all of its stored properties is known. In order for this rule to be satisfied, a designated initializer must make sure that all of its own properties are initialized before it hands off up the chain.

    如上所述,一个对象的内存只有在其所有存储型属性确定之后才能完全初始化。为了满足这一规则,指定构造器必须保证它所在类引入的属性在它往上代理之前先完成初始化。

    Safety check 2 (安全检查二)

    A designated initializer must delegate up to a superclass initializer before assigning a value to an inherited property. If it doesn’t, the new value the designated initializer assigns will be overwritten by the superclass as part of its own initialization.

    指定构造器必须先向上代理调用父类构造器,然后再为继承的属性设置新值。如果没这么做,指定构造器赋予的新值将被父类中的构造器所覆盖。

    Safety check 3 (安全检查三)

    A convenience initializer must delegate to another initializer before assigning a value toanyproperty (including properties defined by the same class). If it doesn’t, the new value the convenience initializer assigns will be overwritten by its own class’s designated initializer.

    便利构造器必须先代理调用同一类中的其它构造器,然后再为任意属性赋新值。如果没这么做,便利构造器赋予的新值将被同一类中其它指定构造器所覆盖。

    Safety check 4 (安全检查四)

    An initializer cannot call any instance methods, read the values of any instance properties, or refer toselfas a value until after the first phase of initialization is complete.

    构造器在第一阶段构造完成之前,不能调用任何实例方法,不能读取任何实例属性的值,不能引用self作为一个值。

    The class instance is not fully valid until the first phase ends. Properties can only be accessed, and methods can only be called, once the class instance is known to be valid at the end of the first phase.

    类实例在第一阶段结束以前并不是完全有效的。只有第一阶段完成后,该实例才会成为有效实例,才能访问属性和调用方法。

    Here’s how two-phase initialization plays out, based on the four safety checks above:

    以下是两段式构造过程中基于上述安全检查的构造流程展示:

    Phase 1 (阶段一)

    1. A designated or convenience initializer is called on a class.
    某个指定构造器或便利构造器被调用。

    2. Memory for a new instance of that class is allocated. The memory is not yet initialized.

    完成新实例内存的分配,但此时内存还没有被初始化。

    3. A designated initializer for that class confirms that all stored properties introduced by that class have a value. The memory for these stored properties is now initialized.

    指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化。

    4. The designated initializer hands off to a superclass initializer to perform the same task for its own stored properties.

    指定构造器将调用父类的构造器,完成父类属性的初始化。

    5. This continues up the class inheritance chain until the top of the chain is reached.

    这个调用父类构造器的过程沿着构造器链一直往上执行,直到到达构造器链的最顶部。

    6. Once the top of the chain is reached, and the final class in the chain has ensured that all of its stored properties have a value, the instance’s memory is considered to be fully initialized, and phase 1 is complete.

    当到达了构造器链最顶部,且已确保所有实例包含的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化。此时阶段 1 完成。

    Phase 2 (阶段二)

    1. Working back down from the top of the chain, each designated initializer in the chain has the option to customize the instance further. Initializers are now able to accessselfand can modify its properties, call its instance methods, and so on.

    从顶部构造器链一直往下,每个构造器链中类的指定构造器都有机会进一步定制实例。构造器此时可以访问self、修改它的属性并调用实例方法等等。

    2. Finally, any convenience initializers in the chain have the option to customize the instance and to work withself.

    最终,任意构造器链中的便利构造器可以有机会定制实例和使用self。

    Here’s how phase 1 looks for an initialization call for a hypothetical subclass and superclass:

    下图展示了在假定的子类和父类之间的构造阶段 1:

    In this example, initialization begins with a call to a convenience initializer on the subclass. This convenience initializer cannot yet modify any properties. It delegates across to a designated initializer from the same class.

    在这个例子中,构造过程从对子类中一个便利构造器的调用开始。这个便利构造器此时没法修改任何属性,它把构造任务代理给同一类中的指定构造器。

    The designated initializer makes sure that all of the subclass’s properties have a value, as per safety check 1. It then calls a designated initializer on its superclass to continue the initialization up the chain.

    如安全检查 1 所示,指定构造器将确保所有子类的属性都有值。然后它将调用父类的指定构造器,并沿着构造器链一直往上完成父类的构造过程。

    The superclass’s designated initializer makes sure that all of the superclass properties have a value. There are no further superclasses to initialize, and so no further delegation is needed.

    父类中的指定构造器确保所有父类的属性都有值。由于没有更多的父类需要初始化,也就无需继续向上代理。

    As soon as all properties of the superclass have an initial value, its memory is considered fully initialized, and Phase 1 is complete.

    一旦父类中所有属性都有了初始值,实例的内存被认为是完全初始化,阶段 1 完成。

    Here’s how phase 2 looks for the same initialization call:

    以下展示了相同构造过程的阶段 2:

    The superclass’s designated initializer now has an opportunity to customize the instance further (although it does not have to).

    父类中的指定构造器现在有机会进一步来定制实例(尽管这不是必须的)。

    Once the superclass’s designated initializer is finished, the subclass’s designated initializer can perform additional customization (although again, it does not have to).

    一旦父类中的指定构造器完成调用,子类中的指定构造器可以执行更多的定制操作(这也不是必须的)。

    Finally, once the subclass’s designated initializer is finished, the convenience initializer that was originally called can perform additional customization.

    最终,一旦子类的指定构造器完成调用,最开始被调用的便利构造器可以执行更多的定制操作。

    Initializer Inheritance and Overriding (构造器的继承和重写)

    Unlike subclasses in Objective-C, Swift subclasses do not inherit their superclass initializers by default. Swift’s approach prevents a situation in which a simple initializer from a superclass is inherited by a more specialized subclass and is used to create a new instance of the subclass that is not fully or correctly initialized.

    跟 Objective-C 中的子类不同,Swift 中的子类默认情况下不会继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更精细的子类继承,并被错误地用来创建子类的实例。

    NOTE

    Superclass initializersareinherited in certain circumstances, but only when it is safe and appropriate to do so. For more information, seeAutomatic Initializer Inheritancebelow.

    父类的构造器仅会在安全和适当的情况下被继承。具体内容请参考后续章节构造器的自动继承

    If you want a custom subclass to present one or more of the same initializers as its superclass, you can provide a custom implementation of those initializers within the subclass.

    假如你希望自定义的子类中能提供一个或多个跟父类相同的构造器,你可以在子类中提供这些构造器的自定义实现。

    When you write a subclass initializer that matches a superclassdesignatedinitializer, you are effectively providing an override of that designated initializer. Therefore, you must write theoverridemodifier before the subclass’s initializer definition. This is true even if you are overriding an automatically provided default initializer, as described inDefault Initializers.

    当你在编写一个和父类中指定构造器相匹配的子类构造器时,你实际上是在重写父类的这个指定构造器。因此,你必须在定义子类构造器时带上override修饰符。即使你重写的是系统自动提供的默认构造器,也需要带上override修饰符,具体内容请参考默认构造器

    As with an overridden property, method or subscript, the presence of theoverridemodifier prompts Swift to check that the superclass has a matching designated initializer to be overridden, and validates that the parameters for your overriding initializer have been specified as intended.

    正如重写属性,方法或者是下标,override修饰符会让编译器去检查父类中是否有相匹配的指定构造器,并验证构造器参数是否正确。

    NOTE

    You always write theoverridemodifier when overriding a superclass designated initializer, even if your subclass’s implementation of the initializer is a convenience initializer.

    当你重写一个父类的指定构造器时,你总是需要写override修饰符,即使你的子类将父类的指定构造器重写为了便利构造器。

    Conversely, if you write a subclass initializer that matches a superclassconvenienceinitializer, that superclass convenience initializer can never be called directly by your subclass, as per the rules described above inInitializer Delegation for Class Types. Therefore, your subclass is not (strictly speaking) providing an override of the superclass initializer. As a result, you do not write theoverridemodifier when providing a matching implementation of a superclass convenience initializer.

    相反,如果你编写了一个和父类便利构造器相匹配的子类构造器,由于子类不能直接调用父类的便利构造器(每个规则都在上文类的构造器代理规则有所描述),因此,严格意义上来讲,你的子类并未对一个父类构造器提供重写。最后的结果就是,你在子类中“重写”一个父类便利构造器时,不需要加override前缀。

    The example below defines a base class calledVehicle. This base class declares a stored property callednumberOfWheels, with a defaultIntvalue of0. ThenumberOfWheelsproperty is used by a computed property calleddescriptionto create aStringdescription of the vehicle’s characteristics:

    在下面的例子中定义了一个叫Vehicle的基类。基类中声明了一个存储型属性numberOfWheels,它是值为0的Int类型的存储型属性。numberOfWheels属性用于创建名为descrpiption的String类型的计算型属性:

    class Vehicle{

        var numberOfWheels=0

        var description:String{

        return "\(numberOfWheels)wheel(s)"

      }

    }

    TheVehicleclass provides a default value for its only stored property, and does not provide any custom initializers itself. As a result, it automatically receives a default initializer, as described inDefault Initializers. The default initializer (when available) is always a designated initializer for a class, and can be used to create a newVehicleinstance with anumberOfWheelsof0:

    Vehicle类只为存储型属性提供默认值,而不自定义构造器。因此,它会自动获得一个默认构造器,具体内容请参考默认构造器。自动获得的默认构造器总会是类中的指定构造器,它可以用于创建numberOfWheels为0的Vehicle实例:

    let vehicle=Vehicle()

        print("Vehicle:\(vehicle.description)")

    // Vehicle: 0 wheel(s)

    The next example defines a subclass ofVehiclecalledBicycle:

    下面例子中定义了一个Vehicle的子类Bicycle:

    class Bicycle:Vehicle{

        override init() {

             super.init()

            numberOfWheels=2

        }

    }

    TheBicyclesubclass defines a custom designated initializer,init(). This designated initializer matches a designated initializer from the superclass ofBicycle, and so theBicycleversion of this initializer is marked with theoverridemodifier.

    子类Bicycle定义了一个自定义指定构造器init()。这个指定构造器和父类的指定构造器相匹配,所以Bicycle中的指定构造器需要带上override修饰符。

    Theinit()initializer forBicyclestarts by callingsuper.init(), which calls the default initializer for theBicycleclass’s superclass,Vehicle. This ensures that thenumberOfWheelsinherited property is initialized byVehiclebeforeBicyclehas the opportunity to modify the property. After callingsuper.init(), the original value ofnumberOfWheelsis replaced with a new value of2.

    Bicycle的构造器init()以调用super.init()方法开始,这个方法的作用是调用Bicycle的父类Vehicle的默认构造器。这样可以确保Bicycle在修改属性之前,它所继承的属性numberOfWheels能被Vehicle类初始化。在调用super.init()之后,属性numberOfWheels的原值被新值2替换。

    If you create an instance ofBicycle, you can call its inheriteddescriptioncomputed property to see how itsnumberOfWheelsproperty has been updated:

    如果你创建一个Bicycle实例,你可以调用继承的description计算型属性去查看属性numberOfWheels是否有改变:

    let bicycle=Bicycle()

    print("Bicycle:\(bicycle.description)")

    // Bicycle: 2 wheel(s)

    NOTE

    Subclasses can modify inherited variable properties during initialization, but can not modify inherited constant properties.

    子类可以在初始化时修改继承来的变量属性,但是不能修改继承来的常量属性。

    相关文章

      网友评论

        本文标题:Initialization (构造过程上)

        本文链接:https://www.haomeiwen.com/subject/uqfibttx.html