Swift-构造过程

来源:互联网 发布:2016年8月淘宝消保新规 编辑:程序博客网 时间:2024/05/20 18:02
//: Playground - noun: a place where people can playimport UIKitvar str = "Hello, playground"//存储型属性的初始赋值/* 类和结构体在实例创建时,必须为所有存储型属性设置合适的初始值。存储型属性的值不能处于一个未知的状态。  你可以在构造器中为存储型属性赋初值,也可以在定义属性时为其设置默认值。以下章节将详细介绍这两种方法。  注意: 当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观测器(property observers)。 *///构造器//构造器在创建某特定类型的新实例时调用。它的最简形式类似于一个不带任何参数的实例方法,以关键字init命名。class SomeClass {        init() {                // 在此处执行构造过程    }}struct Fahrenheit {    var temperature: Double    init() {        temperature = 32.0    }}var f = Fahrenheit()f.temperature//默认属性值/* 如前所述,你可以在构造器中为存储型属性设置初始值。同样,你也可以在属性声明时为其设置默认值。  注意: 如果一个属性总是使用同一个初始值,可以为其设置一个默认值。无论定义默认值还是在构造器中赋值,最终它们实现的效果是一样的,只不过默认值将属性的初始化和属性的声明结合的更紧密。使用默认值能让你的构造器更简洁、更清晰,且能通过默认值自动推导出属性的类型;同时,它也能让你充分利用默认构造器、构造器继承(后续章节将讲到)等特性。 *///使用更简单的方式在定义结构体Fahrenheit时为属性temperature设置默认值:struct FahrenheitA {    var temperature = 32.0}//自定义构造过程//构造参数//下面例子中定义了一个包含摄氏度温度的结构体Celsius。它定义了两个不同的构造器:init(fromFahrenheit:)和init(fromKelvin:),二者分别通过接受不同刻度表示的温度值来创建新的实例:struct Celsius {        var temperatureInCelsius: Double = 0.0 ;    init(fromFahrenheit fahrenheit: Double) {                temperatureInCelsius = (fahrenheit - 32.0 )/18 ;    }    init(fromKelvin kelvin: Double) {        temperatureInCelsius = kelvin - 273.15    }    }/* 第一个构造器拥有一个构造参数,其外部名字为fromFahrenheit,内部名字为fahrenheit;第二个构造器也拥有一个构造参数,其外部名字为fromKelvin,内部名字为kelvin。这两个构造器都将唯一的参数值转换成摄氏温度值,并保存在属性temperatureInCelsius中。 */let boilingPointOfWater = Celsius.init(fromFahrenheit: 212.0) ;let freezingPointOfWater = Celsius(fromKelvin: 273.15) ;print("\(boilingPointOfWater)  ,   \(freezingPointOfWater)");/* 参数的内部名称和外部名称  跟函数和方法参数相同,构造参数也存在一个在构造器内部使用的参数名字和一个在调用构造器时使用的外部参数名字。  然而,构造器并不像函数和方法那样在括号前有一个可辨别的名字。所以在调用构造器时,主要通过构造器中的参数名和类型来确定需要调用的构造器。正因为参数如此重要,如果你在定义构造器时没有提供参数的外部名字,Swift 会为每个构造器的参数自动生成一个跟内部名字相同的外部名,就相当于在每个构造参数之前加了一个哈希符号。  以下例子中定义了一个结构体Color,它包含了三个常量:red、green和blue。这些属性可以存储0.0到1.0之间的值,用来指示颜色中红、绿、蓝成分的含量。  Color提供了一个构造器,其中包含三个Double类型的构造参数。Color也可以提供第二个构造器,它只包含Double类型名叫white的参数,它被用于给上述三个构造参数赋予同样的值。 */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    }}//两种构造器都能用于创建一个新的Color实例,你需要为构造器每个外部参数传值。let mentta = Color.init(red: 1.0, green: 0.0, blue: 0.5) ;let halfGray = Color.init(white: 0.5) ;//注意,如果不通过外部参数名字传值,你是没法调用这个构造器的。只要构造器定义了某个外部参数名,你就必须使用它,忽略它将导致编译错误://不带外部名的构造器参数//下面是之前Celsius例子的扩展,跟之前相比添加了一个带有Double类型参数名为celsius的构造器,其外部名用_代替。//调用这种不需要外部参数名称的Celsius(37.0)构造器看起来十分简明的。因此适当使用这种init(_ celsius: Double)构造器可以提供Double类型的参数值而不需要加上外部名。struct Celsius1 {    var temperatureInCelsius: Double = 0.0    init(fromFahrenheit fahrenheit: Double) {        temperatureInCelsius = (fahrenheit - 32.0) / 1.8    }    init(fromKelvin kelvin: Double) {        temperatureInCelsius = kelvin - 273.15    }    init(_ celsius: Double){        temperatureInCelsius = celsius    }}let bodyTemperature = Celsius1.init(37.0);//可选属性类型/* 如果你定制的类型包含一个逻辑上允许取值为空的存储型属性--不管是因为它无法在初始化时赋值,还是因为它可以在之后某个时间点可以赋值为空--你都需要将它定义为可选类型optional type。可选类型的属性将自动初始化为空nil,表示这个属性是故意在初始化时设置为空的。 */class SurveyQuestion {        var text: String    var response : String?    init(text : String){                self.text = text ;    }            func ask() -> Void {                print(self.text) ;    }    }let cheeseQuestion = SurveyQuestion.init(text: "Do you like cheese!") ;cheeseQuestion.ask() ;cheeseQuestion.response = "Yes, I do like cheese." ;//构造过程中常量属性的修改//只要在构造过程结束前常量的值能确定,你可以在构造过程中的任意时间点修改常量属性的值。/* 注意: 对某个类实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。 *///你可以修改上面的SurveyQuestion示例,用常量属性替代变量属性text,指明问题内容text在其创建之后不会再被修改。尽管text属性现在是常量,我们仍然可以在其类的构造器中设置它的值:class SurveyQuestion1 {    let text: String    var response: String?    init(text: String) {        self.text = text    }    func ask() {        print(text)    }}//默认构造器/* Swift 将为所有属性已提供默认值的且自身没有定义任何构造器的结构体或基类,提供一个默认的构造器。这个默认构造器将简单的创建一个所有属性值都设置为默认值的实例。  下面例子中创建了一个类ShoppingListItem,它封装了购物清单中的某一项的属性:名字(name)、数量(quantity)和购买状态 purchase state。 */class ShoppingListItem {    var name: String?    var quantity = 1    var purchased = false}var item = ShoppingListItem()/* 由于ShoppingListItem类中的所有属性都有默认值,且它是没有父类的基类,它将自动获得一个可以为所有属性设置默认值的默认构造器(尽管代码中没有显式为name属性设置默认值,但由于name是可选字符串类型,它将默认设置为nil)。上面例子中使用默认构造器创造了一个ShoppingListItem类的实例(使用ShoppingListItem()形式的构造器语法),并将其赋值给变量item。 *///结构体的逐一成员构造器/* 除上面提到的默认构造器,如果结构体对所有存储型属性提供了默认值且自身没有提供定制的构造器,它们能自动获得一个逐一成员构造器。  逐一成员构造器是用来初始化结构体新实例里成员属性的快捷方法。我们在调用逐一成员构造器时,通过与成员属性名相同的参数名进行传值来完成对成员属性的初始赋值。 *///下面例子中定义了一个结构体Size,它包含两个属性width和height。Swift 可以根据这两个属性的初始赋值0.0自动推导出它们的类型Double。struct Size {        var width = 0.0 ;    var height = 0.0 ;    }struct Point {    var x = 0.0, y = 0.0}let twpByTwo = Size.init(width: 2.0, height: 2.0) ;//值类型的构造器代理//构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能减少多个构造器间的代码重复。/* 构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给本身提供的其它构造器。类则不同,它可以继承自其它类(请参考继承),这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。这些责任将在后续章节类的继承和构造过程中介绍。  对于值类型,你可以使用self.init在自定义的构造器中引用其它的属于相同值类型的构造器。并且你只能在构造器内部调用self.init。  如果你为某个值类型定义了一个定制的构造器,你将无法访问到默认构造器(如果是结构体,则无法访问逐一对象构造器)。这个限制可以防止你在为值类型定义了一个更复杂的,完成了重要准备构造器之后,别人还是错误的使用了那个自动生成的构造器。 *//* 通过以下三种方式为Rect创建实例--使用默认的0值来初始化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) ;            }}/* 第一个Rect构造器init(),在功能上跟没有自定义构造器时自动获得的默认构造器是一样的。这个构造器是一个空函数,使用一对大括号{}来描述,它没有执行任何定制的构造过程。调用这个构造器将返回一个Rect实例,它的origin和size属性都使用定义时的默认值Point(x: 0.0, y: 0.0)和Size(width: 0.0, height: 0.0): */let basicRect = Rect();/*  第二个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)) ;/* 第三个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)) ;/* 构造器init(center:size:)可以自己将origin和size的新值赋值到对应的属性中。然而尽量利用现有的构造器和它所提供的功能来实现init(center:size:)的功能,是更方便、更清晰和更直观的方法。  注意: 如果你想用另外一种不需要自己定义init()和init(origin:size:)的方式来实现这个例子,请参考扩展。 *//* 类的继承和构造过程  类里面的所有存储型属性--包括所有继承自父类的属性--都必须在构造过程中设置初始值。  Swift 提供了两种类型的类构造器来确保所有类实例中存储型属性都能获得初始值,它们分别是指定构造器和便利构造器。 *//* 指定构造器和便利构造器的语法  类的指定构造器的写法跟值类型简单构造器一样:  init(parameters) { statements } 便利构造器也采用相同样式的写法,但需要在init关键字之前放置convenience关键字,并使用空格将它们俩分开:  convenience init(parameters) { statements }  *//* 类的构造器代理规则  为了简化指定构造器和便利构造器之间的调用关系,Swift 采用以下三条规则来限制构造器之间的代理调用:  规则 1  指定构造器必须调用其直接父类的的指定构造器。  规则 2  便利构造器必须调用同一类中定义的其它构造器。  规则 3  便利构造器必须最终以调用一个指定构造器结束。  一个更方便记忆的方法是:  指定构造器必须总是向上代理 便利构造器必须总是横向代理 *//* 指定构造器和便利构造器操作 */class Food {    var name: String    init(name: String) {        self.name = name    }    convenience init() {        self.init(name: "[Unnamed]")    }}let nameMeat = Food.init(name: "Bacon") ;let mysterMeat = Food.init() ;print(nameMeat.name) ;print(mysterMeat.name) ;/* 类层级中的第二个类是Food的子类RecipeIngredient。RecipeIngredient类构建了食谱中的一味调味剂。它引入了Int类型的数量属性quantity(以及从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)    }}

0 0
原创粉丝点击