在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
参考:http://wiki.jikexueyuan.com/project/swift/chapter2/14_Initialization.html 1、构造过程(Initialization)构造过程是为了使用某个类、结构体或枚举类型的实例而进行的准备过程。这个过程包括设置实例中每一个存储属性的值,以及为其执行必要的准备和初始化任务。 构造过程是通过定义构造器( 类的实例也可以通过定义析构器( 2、设置存储属性的初始值类和结构体在实例创建时,必须为所有存储型属性设置合适的初始值。存储型属性的值不能处于一个未知的状态。 可以在构造器中为存储型属性赋初值,也可以在定义属性时为其设置默认值。 注意:在构造器设置存储属性的值,或者定义存储属性默认值,这些都会直接设置值,不会调用属性观察器。 (1)构造器构造器在创建某特定类型的新实例时调用。最简形式如下: 1 init() { 2 // perform some initialization here 3 } 下面是一个结构体的初始化示例: 1 struct Fahrenheit { 2 var temperature: Double 3 init() { 4 temperature = 32.0 5 } 6 } 7 var f = Fahrenheit() 8 print("The default temperature is \(f.temperature)° Fahrenheit") 9 // prints "The default temperature is 32.0° Fahrenheit" (2)默认初始值可以在属性声明时为其设置默认值。 注意:如果一个属性总是使用同一个初始值,可以为其设置一个默认值,而不是在构造器中赋值。尽管它们实现的效果是一样的,只不过默认值将属性的初始化和属性的声明结合的更紧密。使用默认值能让你的构造器更简洁、更清晰,且能通过默认值自动推导出属性的类型;同时,它也能让你充分利用默认构造器、构造器继承等特性。 1 struct Fahrenheit { 2 var temperature = 32.0 3 } 3、自定义构造过程可以通过输入参数和可选属性类型来定义构造过程,也可以在构造过程中修改常量属性。 (1)构造参数可以在定义构造器时提供构造参数,为其提供自定义构造所需值的类型和名字。构造器参数的功能和语法跟函数和方法参数相同。 1 struct Celsius { 2 var temperatureInCelsius: Double 3 init(fromFahrenheit fahrenheit: Double) { 4 temperatureInCelsius = (fahrenheit - 32.0) / 1.8 5 } 6 init(fromKelvin kelvin: Double) { 7 temperatureInCelsius = kelvin - 273.15 8 } 9 } 10 let boilingPointOfWater = Celsius(fromFahrenheit: 212.0) 11 // boilingPointOfWater.temperatureInCelsius is 100.0 12 let freezingPointOfWater = Celsius(fromKelvin: 273.15) 13 // freezingPointOfWater.temperatureInCelsius is 0.0 (2)参数名和变量标签(Parameter Names and Argument Labels)跟函数和方法参数相同,构造参数也存在一个在构造器内部使用的参数名字和一个在调用构造器时使用的外部参数名字。 然而,构造器并不像函数和方法那样在括号前有一个可辨别的名字。所以在调用构造器时,主要通过构造器中的参数名和类型来确定需要调用的构造器。 如果你在定义构造器时没有提供参数的外部名,Swift 会为每个构造器的参数自动生成一个跟内部名相同的外部名。 1 struct Color { 2 let red, green, blue: Double 3 init(red: Double, green: Double, blue: Double) { 4 self.red = red 5 self.green = green 6 self.blue = blue 7 } 8 init(white: Double) { 9 red = white 10 green = white 11 blue = white 12 } 13 } 两个构造器都可以用来创建实例: 1 let magenta = Color(red: 1.0, green: 0.0, blue: 1.0) 2 let halfGray = Color(white: 0.5) 注意,不能不用外部名来调用这些构造器,如果外部名被定义,就一定要使用,省略将会造成编译错误: 1 let veryGreen = Color(0.0, 1.0, 0.0) 2 // this reports a compile-time error - external names are required (3)没有外部名的构造器如果使用构造器时不想用外部名,则定义参数的时候用_代替显式外部名,覆盖默认的外部名。 1 struct Celsius { 2 var temperatureInCelsius: Double 3 init(fromFahrenheit fahrenheit: Double) { 4 temperatureInCelsius = (fahrenheit - 32.0) / 1.8 5 } 6 init(fromKelvin kelvin: Double) { 7 temperatureInCelsius = kelvin - 273.15 8 } 9 init(_ celsius: Double) { 10 temperatureInCelsius = celsius 11 } 12 } 13 let bodyTemperature = Celsius(37.0) 14 // bodyTemperature.temperatureInCelsius is 37.0 (4)可选属性类型可选类型的属性将自动初始化为空 1 class SurveyQuestion { 2 var text: String 3 var response: String? 4 init(text: String) { 5 self.text = text 6 } 7 func ask() { 8 print(text) 9 } 10 } 11 let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?") 12 cheeseQuestion.ask() 13 // prints "Do you like cheese?" 14 cheeseQuestion.response = "Yes, I do like cheese." 调查问题在问题提出之后,我们才能得到回答。所以我们将属性回答 (5)构造过程中给常量属性赋值只要在构造过程结束前常量的值能确定,你可以在构造过程中的任意时间点修改常量属性的值。 注意:对某个类实例(class instances)来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。 1 class SurveyQuestion { 2 let text: String 3 var response: String? 4 init(text: String) { 5 self.text = text 6 } 7 func ask() { 8 print(text) 9 } 10 } 11 let beetsQuestion = SurveyQuestion(text: "How about beets?") 12 beetsQuestion.ask() 13 // prints "How about beets?" 14 beetsQuestion.response = "I also like beets. (But not with cheese.)" 4、默认构造器Swift 为所有属性已提供默认值、并且自身没有定义任何构造器的结构体或基类,提供一个默认的构造器。这个默认构造器将简单的创建一个所有属性值都设置为默认值的实例。 1 class ShoppingListItem { 2 var name: String? 3 var quantity = 1 4 var purchased = false 5 } 6 var item = ShoppingListItem() 由于 (1)结构体类型的逐一成员构造器如果结构体对所有存储型属性提供了默认值且自身没有提供定制的构造器,它们能自动获得一个逐一成员构造器。 和默认构造器不同,结构体接收逐一成员构造器即使它有些存储属性还没有初始值。 逐一成员构造器是用来初始化结构体新实例里成员属性的快捷方法。我们在调用逐一成员构造器时,通过与成员属性名相同的参数名进行传值来完成对成员属性的初始赋值。 1 struct Size { 2 var width = 0.0, height = 0.0 3 } 4 let twoByTwo = Size(width: 2.0, height: 2.0) 5、值类型的构造器代理(Initializer Delegation for Value Types)构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能减少多个构造器间的代码重复。 构造器代理的实现规则和形式在值类型和类类型中有所不同。 值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给本身提供的其它构造器。 类则不同,它可以继承自其它类,这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。 对于值类型,在写自定义的构造器时可以用self.init引用其它的属于相同值类型的构造器。并且你只能在构造器内部调用self.init 如果你为某个值类型定义了一个定制的构造器,你将无法访问到默认构造器(如果是结构体,则无法访问逐一对象构造器)。这个限制可以防止你在为值类型定义了一个更复杂的,完成了重要准备构造器之后,别人还是错误的使用了那个自动生成的构造器。 注意:如果你想通过默认构造器、逐一对象构造器以及你自己定制的构造器为值类型创建实例,我们建议你将自己定制的构造器写到扩展( 1 struct Size { 2 var width = 0.0, height = 0.0 3 } 4 struct Point { 5 var x = 0.0, y = 0.0 6 } 你可以通过以下三种方式为 1 struct Rect { 2 var origin = Point() 3 var size = Size() 4 init() {} 5 init(origin: Point, size: Size) { 6 self.origin = origin 7 self.size = size 8 } 9 init(center: Point, size: Size) { 10 let originX = center.x - (size.width / 2) 11 let originY = center.y - (size.height / 2) 12 self.init(origin: Point(x: originX, y: originY), size: size) 13 } 14 } 第一个 1 let basicRect = Rect() 2 // basicRect's origin is (0.0, 0.0) and its size is (0.0, 0.0) 第二个 1 let originRect = Rect(origin: Point(x: 2.0, y: 2.0), 2 size: Size(width: 5.0, height: 5.0)) 3 // originRect's origin is (2.0, 2.0) and its size is (5.0, 5.0) 第三个 1 let centerRect = Rect(center: Point(x: 4.0, y: 4.0), 2 size: Size(width: 3.0, height: 3.0)) 3 // centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)
6、类的继承和构造过程类里面的所有存储型属性--包括所有继承自父类的属性--都必须在构造过程中设置初始值。 Swift 提供了两种类型的类构造器来确保所有类实例中存储型属性都能获得初始值,分别是指定构造器和便利构造器。 (1)指定构造器(Designated Initializers)和便利构造器(Convenience Initializers)指定构造器是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。 每一个类都必须拥有至少一个指定构造器。在某些情况下,许多类通过继承了父类中的指定构造器而满足了这个条件。 便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入的实例。 你应当只在必要的时候为类提供便利构造器,比方说某种情况下通过使用便利构造器来快捷调用某个指定构造器,能够节省更多开发时间并让类的构造过程更清晰明了。 (2)指定构造器和便利构造器语法指定构造器的语法和值类型的一般构造器相同: 1 init(parameters) { 2 statements 3 } 便利构造器则需要在init关键字前面加上convenience关键字: 1 convenience init(parameters) { 2 statements 3 } (3)类类型的构造器代理(Initializer Delegation for Class Types)为了简化指定构造器和便利构造器之间的调用关系,Swift 采用以下三条规则来限制构造器之间的代理调用:
一个更方便记忆的方法是:
注意:这些规则不会影响使用时,如何用类去创建实例。这些规则只在实现类的定义时有影响。
(4)两段式构造过程(Two-Phase Initialization)Swift 中类的构造过程包含两个阶段。第一个阶段,每个存储型属性通过引入它们的类来设置初始值。当每一个存储型属性值被确定后,第二阶段开始,每个类有一次机会在新实例准备使用之前进一步定制它们的存储型属性。 注意:Swift的两段式构造过程跟 Objective-C 中的构造过程类似。最主要的区别在于阶段 1,Objective-C 给每一个属性赋值 Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程能顺利完成: 安全检查 1指定构造器必须保证它所在类引入的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中的构造器。 一个对象的内存只有在其所有存储型属性确定之后才能完全初始化。为了满足这一规则,指定构造器必须保证它所在类引入的属性在它往上代理之前先完成初始化。 安全检查 2指定构造器必须先向上代理调用父类构造器,然后再为继承的属性设置新值。如果没这么做,指定构造器赋予的新值将被父类中的构造器所覆盖。 安全检查 3便利构造器必须先代理调用同一类中的其它构造器,然后再为任意属性赋新值。如果没这么做,便利构造器赋予的新值将被同一类中其它指定构造器所覆盖。 安全检查 4构造器在第一阶段构造完成之前,不能调用任何实例方法、不能读取任何实例属性的值,
类实例在第一阶段结束以前并不是完全有效,仅能访问属性和调用方法,一旦完成第一阶段,该实例才会声明为有效实例。 以下是两段式构造过程: 阶段 1
阶段 2
(5)构造器的继承和重写 跟 Objective-C 中的子类不同,Swift 中的子类不会默认继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更专业的子类继承,并被错误的用来创建子类的实例。 注意: 父类的构造器仅在确定和安全的情况下被继承。 假如你希望自定义的子类中能实现一个或多个跟父类相同的构造器,也许是为了完成一些定制的构造过程,你可以在你定制的子类中提供和重写与父类相同的构造器。 当你写一个和父类指定构造器相同的子类构造器时,你需要重写这个指定的构造器。须在定义子类构造器时带上 无论是重写属性,方法或者是subscript,只要含有 注意:当你重写一个父类指定构造器时,你总是需要写 相反地,如果你写了一个和父类便利构造器相匹配的子类构造器,子类都不能直接调用父类的便利构造器,因此,严格来说,子类没有复写父类的便利构造器,因此,这里不必加override修饰符。 下面是一个基类: 1 class Vehicle { 2 var numberOfWheels = 0 3 var description: String { 4 return "\(numberOfWheels) wheel(s)" 5 } 6 }
1 let vehicle = Vehicle() 2 print("Vehicle: \(vehicle.description)") 3 // Vehicle: 0 wheel(s) 下面来定义一个子类: 1 class Bicycle: Vehicle { 2 override init() { 3 super.init() 4 numberOfWheels = 2 5 } 6 } Bicycle类定制了一个构造器init(),这个构造器和父类的指定构造器匹配,因此要加上override关键字。
下面创建一个Bicycle的实例: 1 let bicycle = Bicycle() 2 print("Bicycle: \(bicycle.description)") 3 // Bicycle: 2 wheel(s) 注意:子类可以在初始化时修改继承变量属性,但是不能修改继承过来的常量属性。 (6)自动构造器的继承 如上所述,子类不会默认继承父类的构造器。但是如果特定条件可以满足,父类构造器是可以被自动继承的。在实践中,这意味着对于许多常见场景你不必重写父类的构造器,并且在尽可能安全的情况下以最小的代价来继承父类的构造器。 假设要为子类中引入的任意新属性提供默认值,适用以下2个规则: 规则 1如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。 规则 2如果子类提供了所有父类指定构造器的实现--不管是通过规则1继承过来的,还是通过自定义实现的--它将自动继承所有父类的便利构造器。 即使你在子类中添加了更多的便利构造器,这两条规则仍然适用。 注意:子类可以通过满足规则2的方式,使用子类便利构造器来实现父类的指定构造器。 8、指定构造器和便利构造器的操作 下面展示指定构造器、便利构造器和自动构造器继承步骤。包括了 基类Food: 1 class Food { 2 var name: String 3 init(name: String) { 4 self.name = name 5 } 6 convenience init() { 7 self.init(name: "[Unnamed]") 8 } 9 } 它的构造器链是:便利构造器init()调用指定构造器init(name:String). 类没有提供一个默认的逐一成员构造器,所以 1 let namedMeat = Food(name: "Bacon") 2 // namedMeat's name is "Bacon"
1 let mysteryMeat = Food() 2 // mysteryMeat's name is "[Unnamed]" 下面是 1 class RecipeIngredient: Food { 2 var quantity: Int 3 init(name: String, quantity: Int) { 4 self.quantity = quantity 5 super.init(name: name) 6 } 7 override convenience init(name: String) { 8 self.init(name: name, quantity: 1) 9 } 10 }
注意, 在这个例子中, 所有的这三种构造器都可以用来创建新的 1 let oneMysteryItem = RecipeIngredient() 2 let oneBacon = RecipeIngredient(name: "Bacon") 3 let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6) 下面是 购物单中的每一项总是从 1 class ShoppingListItem: RecipeIngredient { 2 var purchased = false 3 var description: String { 4 var output = "\(quantity) x \(name)" 5 output += purchased ? " ✔" : " ✘" 6 return output 7 } 8 } 注意: 由于它为自己引入的所有属性都提供了默认值,并且自己没有定义任何构造器, 1 var breakfastList = [ 2 ShoppingListItem(), 3 ShoppingListItem(name: "Bacon"), 4 ShoppingListItem(name: "Eggs", quantity: 6), 5 ] 6 breakfastList[0].name = "Orange juice" 7 breakfastList[0].purchased = true 8 for item in breakfastList { 9 print(item.description) 10 } 11 // 1 x Orange juice ✔ 12 // 1 x Bacon ✘ 13 // 6 x Eggs ✘ 9、可失败的构造器 如果一个类,结构体或枚举类型的对象,在构造自身的过程中有可能失败,则为其定义一个可失败构造器是非常有用的。这里所指的“失败”是指,如给构造器传入无效的参数值,或缺少某种所需的外部资源,又或是不满足某种必要的条件等。 为了妥善处理这种构造过程中可能会失败的情况。你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在 注意: 可失败构造器的参数名和参数类型,不能与其它非可失败构造器的参数名,及其类型相同 可失败构造器,在构建对象的过程中,创建一个其自身类型为可选类型的对象。你通过 注意: 严格来说,构造器都不支持返回值。因为构造器本身的作用,只是为了能确保对象自身能被正确构建。所以即使你在表明可失败构造器,失败的这种情况下,用到了 1 struct Animal { 2 let species: String 3 init?(species: String) { 4 if species.isEmpty { return nil } 5 self.species = species 6 } 7 } 该结构体还定义了一个,带一个 可以通过该可失败构造器来构建一个Animal的对象,并检查其构建过程是否成功: 1 let someCreature = Animal(species: "Giraffe") 2 // someCreature is of type Animal?, not Animal 3 4 if let giraffe = someCreature { 5 print("An animal was initialized with a species of \(giraffe.species)") 6 } 7 // prints "An animal was initialized with a species of Giraffe" 如果给该可失败构造器传入一个空字符串作为其参数,则该可失败构造器失败。 1 let anonymousCreature = Animal(species: "") 2 // anonymousCreature is of type Animal?, not Animal 3 4 if anonymousCreature == nil { 5 print("The anonymous creature could not be initialized") 6 } 7 // prints "The anonymous creature could not be initialized" 注意:空字符串( (1)枚举类型的可失败构造器 可以通过构造一个带一个或多个参数的可失败构造器来获取枚举类型中特定的枚举成员。参数不满足你所期望的条件时,构造失败。 1 enum TemperatureUnit { 2 case Kelvin, Celsius, Fahrenheit 3 init?(symbol: Character) { 4 switch symbol { 5 case "K": 6 self = .Kelvin 7 case "C": 8 self = .Celsius 9 case "F": 10 self = .Fahrenheit 11 default: 12 return nil 13 } 14 } 15 } 你可以通过给该可失败构造器传递合适的参数来获取这三个枚举成员中相匹配的其中一个枚举成员。如果不匹配,则构造失败: 1 let fahrenheitUnit = TemperatureUnit(symbol: "F") 2 if fahrenheitUnit != nil { 3 print("This is a defined temperature unit, so initialization succeeded.") 4 } 5 // prints "This is a defined temperature unit, so initialization succeeded." 6 7 let unknownUnit = TemperatureUnit(symbol: "X") 8 if unknownUnit == nil { 9 print("This is not a defined temperature unit, so initialization failed.") 10 } 11 // prints "This is not a defined temperature unit, so initialization failed." (2)带原始值的枚举类型的可失败构造器 带原始值的枚举类型会自带一个可失败构造器 |
请发表评论