Swift2.1-初始化
初始化Initialization是为准备使用类,结构体或者枚举实例的一个过程。这个过程涉及了在实例里的每一个存储属性设置一个初始值,以及在新实例准备使用之前执行任何其他所必须的设置或初始化。 你通过定义初始化器实现这次初始化过程,这是一个特殊的方法可以被调用来创建一个新的实例。不向Objective-C的初始化器,Swift初始化器没有返回值。这些初始化器主要的角色就是确保在第一次使用的时候,能过正确的创建一个新的实例。 类实例也可以实现一个反初始化器,在类的实例被释放之前,反初始化器会执行所有的自定义清理。更多关于反初始化器的信息,请看反初始化器。 为存储属性设置初始化值在一个类或结构体的实例被创建的时候,类和结构体必须为所有的存储属性设置一个合适的初始值。存储属性不能放在不确定的状态中。 你可以在初始化器里为存储属性设置一个初始值,或者通过分配一个默认的属性值作为属性定义的一部分。在下面会描述这些行为。 注意:当你个一个存储属性分配默认值,或者在一个初始化器里设置它的初始值的时候,属性值就会被直接设置,不用调用任何属性监听者。 初始化器初始化器被调用来创建一个新的特殊类型的实例。以最简单的形式,一个初始化器就像一个没有形式参数的实例方法,使用init关键字: init() {
//perform some initialization here
}
下面的??定义了一个名为 struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
print("The default temperature (f.temperature)° Fahrenheit")
//prints "The default temperature is 32.0° Fahrenheit"
这个结构体定了一个单一的没有形式参数的初始化器 默认的属性值你可以在初始化器里为存储属性设置初始值。另外,指定一个默认属性值作为属性的声明。当属性被定义的时候,你可以通过为这个属性分配一个初始值来指定默认的属性值。 注意:如果一个属性一直保持相同的初始值,可以提供一个默认值,而不是在初始化器里设置这个值。最终结果是一样的,但是默认值将属性的初始化更紧密地联系到它的声明。它使得你的初始化器更短更清晰,并且可以让你根据默认值推断属性的类型。默认值也让你利用默认初始化和初始化继承更容易。例如本章后面。 在属性声明这一点上,通过提供 struct Fahrenheit {
var temperature = 32.0
}
自定义初始化你可以通过输入形式参数和可选(optional)类型自定义初始化过程,或者在初始化的时候分配常量属性,下面的部分将会描述。 初始化形式参数你可以提供初始化形式参数作为初始化器的一部分, 下面的??定义了一个名为 struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: 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
第一个初始化器有一个初始化形式参数,并且有一个外部变量名 局部和外部形式参数名与函数和方法形式参数一样,初始化形式参数也可以在初始化器内部有一个局部变量名来使用,然后当调用这个初始化器的时候,会使用外部变量名。 然而,如果初始化器的圆括号阻碍了函数和方法的执行,初始化器不能确定函数名。因此,一个初始化器的参数名称和类型在识别初始化器该调用的时候扮演了一个非常重要的角色。因为这个原因,如果你没有提供一个外部名,Swift在初始化的时候为每一个形式参数自动提供了一个外部名称。 下面的??定义了一个名为
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
}
}
初始化器可以被用来创建一个新的 let magenta = Color(red: 1.0,green: 0.0,blue: 1.0)
let halfGray = Color(white: 0.5)
需要注意的是,没有外部名称是不可能调用这些初始化器的。外部参数名如果被定义了,那么必须用在初始化器里,省略的话会报一个编译时(compile-tim)错误: let veryGreen = Color(0.0,1.0,0.0)
// this reports a compile-time error - external names are required
初始化器形式参数不用外部名称如果你不想为初始化器形式参数使用外部名称,可以写一个下划线(_)为那个形式参数替代明确的外部名称,从而可以重写默认的行为。 这里有一个扩大的 struct Celsius {
var temperatureInCelsius:Double
init(fromFahrengeit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKevlvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
init(_ celsius: Double) {
temperatureInCelsius = celsius
}
}
let bodyTemperature = Celsius(37.0)
// bodyTemperature.temperatureInCelsius is 37.0
初始化器调用 可选的属性类型如果你的自定义类型有一个存储属性,逻辑上是允许"无值(no value)"的--大概因为它的值在初始化期间不能被设置,或者因为它以后会被允许可以"无值"--声明属性为可选类型。可选类型的属性自动地初始化为 下面的??定义了一个名为 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."
对调查问题的回答直到被问的时候才能知道,所以 在初始化期间分配常量属性在初始化期间,你可以给常量属性分配一个值,只要等到初始化完成的时候,它就会被设置一个确定的值。一旦为一个常量属性分配了一个值,它就不能再被修改了。 注意:对于类实例来说,一个常量属性在初始化期间只能通过类引用(introduces)来修改,它不能被子类修改。 在上面 class SurveyQuestion {
let text: String
var response: String?
init() {
self.text = text
}
func ask() {
print(text)
}
}
let beetQuestion = SurveyQuestion(text: "How about beets?")
beetsQuestion.ask()
// prints "How about beets?"
beetsQuestion.response = "I also like beets. (But not with cheese.)"
默认初始化器Swift为所有的结构体或类提供了一个默认的初始化器。这个默认的初始化器给所有的属性提供了默认值。这个默认的初始化器简单地创建了一个新的实例,这个实例的所有属性都有一个默认值。 下面的??定义了一个名为 class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
由于 结构体类型的成员初始化器如果没有定义任何自定义的初始化器,结构体类型会自动获得一个成员初始化器。不像默认的初始化器,结构体会接收一个成员初始化器,即使它的存储属性没有默认值。 这个成员初始化器是一个快速初始化新结构体实例成员属性的方式。新实例的属性初始值可以通过名称传递到成员初始化器里。 下面的??定义了一个名为
struct Size {
var width = 0.0,height = 0.0
}
let twoByTwo = Size(width: 2.0,height: 2.0)
值类型的初始化委托(代理)初始化器可以调用其他初始化器来执行部分实例的初始化。这个过程,看做是初始化器委托(代理),避免了通过多个初始化器复制代码。 初始化器委托(代理)如何工作以及允许那些形式的委托(代理),这些规则对于值类型和类类型是不同的。值类型(包括结构体和枚举)不支持继承,所以他它们的初始化器委托(代理)的过程相当简单,因为它们只能提供它们自己为另一个初始化器委托(代理)。然而,类可以从其他类里继承,请看有关继承(Inheritance)的描述。这就意味着类有一些其他的任务(responsibilities)来确保它们继承的所有存储属性在初始化期间都分配了一个合适的值。这些任务描述在(类的继承和初始化)Class Inheritance and Initialization里。 对于值类型,当你写自己自定义的初始化器的时候,你可以使用 注意,如果你为值类型定义了自定义的初始化器,你就不能访问那个类型默认的初始化器(或者结构体是成员初始化器)。这个限制防止初始化器提供的额外必要配置被别人意外地使用自动初始化器替代掉。 注意:如果你想要使用默认的初始化器和成员初始化器以及你自定义的初始化器来初始化你的自定义值类型,你可以在扩展(extension)里写自定义初始化器而不是把值类型的原始实现作为它的一部分。想要了解更多的信息,请看扩展(Extensions)。 下面的??定义了一个自定义的 struct Size {
var width = 0.0,height = 0.0
}
struct Point {
var x = 0.0,y = 0.0
}
你可以初始化 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)
}
}
第一个 let basicRect = Rect()
// basicRect's origin is (0.0,0.0) and its size is (0.0,0.0)
第二个 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)
第三个 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)
注意:另一种是方法不定义 类继承和初始化所有类的存储属性–包括从它的父类继承的任何属性–必须在初始化期间分配一个初始值。 Swift为类类型定义了两种初始化器,帮助确保所有的存储属性接收一个初始值。这些被称为指定初始化器和方便初始化器。 指定初始化器和便利初始化器指定初始化器是类主要的初始化器。一个指定的初始化器可以初始化所有介绍那个类的属性,并且可以调用一个合适的父类初始化器来继续这个初始化过程给父类链。 类往往很少指定初始化器,并且一个类只有一个指定初始化器也是相当普遍的。指定初始化器是初始化开始并持续初始化过程到父类链的“传送”点。 每个类必须至少有一个指定的初始化器。在某些情况下,这就需要通过从父类的一个或更多的指定初始化器继承来满足这个要求,在自动初始化器继承(Automatic Initializer Inheritance)里有描述。 便利初始化器(Convenience initializers)是第二个,为一个类支持初始化器。你可以从相同的类里定义一个便利初始化器,来调用一个指定的初始化器作为便利初始化器,此便利初始化器将指定初始化器的形式参数设置为了默认值。你也可以为具体的使用情况或输入的值类型,定义一个便利初始化器,从而创建一个类的实例。 如果你的类不需要便利初始化器,那么你可以不用。创建便利初始化器时,常见初始化方式将会节省时间或者让类初始化的意图更清楚。 指定初始化器和便利初始化器的语法用相同的方式来写类的指定初始化器作为值类型的简单初始化器: init(parameters) { statements }
便利初始化器有着相同的书写方式,但是要用 convenience init(parameters) { statements }
类类型的初始化器委托/代理为了简化指定和便利初始化器之间的调用关系,Swift在初始化器之间的委托/代理调用有下面的三个规则: 规则 1 规则 2 规则 3 一个简单记忆的这些规则的方法如下:
下面图中表示了这些规则: 如图所示,父类包含一个指定初始化器和两个便利初始化器。一个便利初始化器调用另一个便利初始化器,而后者又调用了指定初始化器。这满足了规则2和规则3。所有的指定初始化器必须从父类调用指定初始化器,这就满足了规则1。 注意:这些规则不会影响每个类创建实例的使用。任何上图的初始化器都可以用来完整创建对应类的实例。这个规则只在类的实现时有影响。 下图展示了更复杂的类的层级结构。它演示了指定初始化器是如何在此层级结构中充当”管道”作用。在类的初始化链上简化了类之间的内部关系: 两段式初始化器Swift的类初始化器包含两个过程。在第一个阶段,通过引入类的初始化器为每一个存储属性分配了一个初始值。一旦每个存储属性的值确定后,第二个阶段就开始了,它给每个类一次机会在新的实例准备使用之前来定制它的存储属性。 两段式初始化过程的使用让初始化更加安全,同时在每个类的层级结构给与了每个类完全的灵活性。两段式初始化过程可以防止属性值在初始化之前被访问,还可以防止属性值被另一个初始化器意外地赋予不同的值。 注意:Swift的两段式初始化过程和Objective-C的初始化相似。主要的不同是在第一阶段,Objective-C为每一个属性分配 0 或空值值(例如0或nil)。Swift的初始化过程更加灵活,它允许你设置自定义的初始值,并可以自如应对0或nil作为合法值的情况。 Swift编译器执行四种有效的安全检查来确保两段式初始化过程能够顺利完成: 安全检查 1 指定初始化器必须保证在向上委托父类初始化器之前,其所在类引入的所有属性都要初始化完成。 如上所述,一个对象的内存只有在其所有存储型属性确定之后才能完全初始化。为了满足这一规则,指定初始化器必须保证它所在类引入的属性在它往上代理之前先完成初始化。 安全检查 2 指定构造器必须先向上代理调用父类初始化器,然后再为继承的属性设置新值。如果不这样做,指定构造器赋予的新值将被父类中的初始化器所覆盖。 安全检查 3 便利初始化器必须先代理调用同一类中的其它初始化器,然后再为任意属性赋新值。如果没这么做,便利构初始化器赋予的新值将被同一类中其它指定初始化器所覆盖。 安全检查 4 初始化器在第一阶段构造完成之前,不能调用任何实例方法、不能读取任何实例属性的值,也不能引用 第一阶段结束的时候类的实例并不是完全合法的。属性只能被读取,方法也只能被调用,直到第一阶段结束的时候,这个类实例才被看做是合法的。 以下是两段初始化过程基于上述四种检查的流程: 阶段 1
阶段 2
下面展示了第一阶段假定的子类如何调用父类: 在这个例子,初始化过程从一个子类的便利初始化器开始。这个便利初始化器还不能修改任何属性。它通过同一个类的初始化器来实现代理。 指定初始化器确保所有的子类属性都有值,如安全检查1。然后它将调用父类的指定初始化器,并沿着初始化器链一直往上完成父类的初始化过程。 父类的指定初始化器确保所有的父类属性都有值。由于没有更多的父类来初始化,也就不需要更多的代理。 一旦父类中所有属性都有初始值,它的内存就被认为完全初始化了,第一个过程完成。 下图是第二阶段相同的初始化过程: 现在父类的指定初始化器有机会来定制更多实例(尽管没有这种必要)。 一旦父类的指定初始化器完成了调用,子类的指定初始化器就可以执行额外的定制(同样,尽管没有这种必要)。 最后,一旦子类的指定初始化器完成调用,便利初始化器将会执行更多额外的定制操作。 初始化器的继承和重载不像在Objective-C中的子类,Swift的子类不会默认继承父类的初始化器。Swift的这种机制防止了一个从父类简单的初始化器被一个更专业的子类初继承,并被用来错误的创建一个新的实例。 注意:在某些情况是继承父类初始化器,但只有这样是安全且合适的时候。想要了解更多信息,请看Automatic Initializer Inheritance。 如果你想自定义子类能实现一个或多个和父类相同的初始化器,你可以在子类中为那些初始化器提供定制的实现。 当你写了匹配父类指定初始化器的子类初始化器的时候,你可以重写那个初始化器。因此,在子类的初始化器定义之前,你必须写 作为一个重写的属性,方法或下标脚本, 注意:当重写父类指定初始化器时,你必须写 相反,如果你写了一个匹配父类便利初始化器的子类初始化器,父类的便利初始化器将不会在你的子类直接调用,相关描述请看类的初始化代理。因此,子类(严格的说)不会提供一个父类初始化器的重写。当提供一个匹配的父类便利初始化器的实现时,你不用谢 下面的例子定义了一个名为 class Vehicle {
var numberOfWheels = 0
var description: String {
return "(numberOfWheels) wheel(s)"
}
}
let vehicle = Vehicle()
print("Vehicle: (vehicle。description)")
// Vehicle: 0 wheel(s)
下面的例子定义了一个名为 class Bicycle: Vehicle {
override init() {
super.init()
numberOfWheels = 2
}
}
子类
如果你创建一个 let bicycle = Bicycle()
print("Bicycle: (bicycle.description)")
// Bicycle: 2 wheel(s)
注意子类可以在初始化时修改继承变量属性,但是不能修改继承过来的常量属性。 自动初始化器的继承如上所述,子类默认不会继承父类初始化器。然而,如果在特定时间,父类初始化器是可以自动继承的。在实践环境中,这意味着在许多场景中你不必重写父类初始化器,并且要以尽可能安全的方式来继承父类的初始化器。 假设要为子类中引入的任意新属性提供默认值,请遵守以下2个规则: 规则1 规则2 如果你的子类提供了父类指定初始化器的实现–不管是通过规则1继承来的,还是通过自定义实现的–那么它自动继承所有的父类便利初始化器。 尽管你的子类添加了更多的便利初始化器,这些规则仍然适用。 注意:当子类的便利初始化器部分满足规则2的时候,子类可以实现父类指定初始化器。 指定和便利初始化器的操作下面的??展示了在操作中指定初始化器,便利初始化器和自动初始化器的继承。这个??定义了一个包含三个类,分别是 在层次关系中的基类称为 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"
类
let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]"
类层级中的第二个类是Food的子类 class RecipeIngredient: Food { var quartity: Int init(name: String,quantity: Int) { self.quantity = quantity super.init(name: name) } override convenience init(name: String) { self.init(name: name,quantity: 1) } }
下面的图标展示了
尽管 在这个??中, 所有的这三种初始化器都可以用来创建新的 let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs",quantity: 6)
类层级中第三个也是最后一个类是 在购物表里每一项都是从未购买(unpurchased)状态开始的。为了展现这一事实, class ShoppingListItem: RecipeIngredient {
var purchased = flase
var desription: String {
var output = "(quantity) x (name.lowercaseString)"
output += purchased ? " ?" : " ?"
return output
}
}
注意:
由于它为自己引入的所有属性提供了一个默认值,并且自己没有定义任何初始化器, 下图展示了三类的初始化链: 你可以使用全部三个继承来的构造器来创建 var breakfastList = [
ShoppingListItem(),
ShoppingListItem(name: "Bacon"),
ShoppingListItem(name: "Eggs",quantity: 6),]
breakfastList[0].name = "Orange juice"
breakfastList[0].purchased = true
for item in breakfastList {
print(item.description)
} // 1 x orange juice ? // 1 x bacon ? // 6 x eggs ?
如上所述,通过一个字面量的方式创建了包含三个 可失败初始化器定义一个类,结构体或枚举有事在初始化的时候会失败。这个失败可能由以下几种方式触发,包括给初始化传入无效的形式参数值,或缺少某种外部所需的资源,又或是不满足某种必要的条件等。 为了妥善处理这种可能失败的情况,可以在 注意:你不能定义一个可失败的和非可失败的初始化器有相同形式参数的类型和名称。 一个可失败的初始化器创建了一个可选类型的值。你通过 注意:严格来说,初始化器不会有返回值。相反,它们的角色是确保在初始化结束时, 下面的??定义了一个名为 struct Animal {
let speices: Stirng
init?(speices: String) {
if species.isEmpty { return nil }
self.species = species
}
}
你可以通过可失败初始化器来初始化一个新的 let someCreature = Animal(species: "Giraffe")
// someCreature is of type Animal?,not Animal
if let giraffe = someCreature {
print("An animal was initialized with a species of (giraffe.species)")
}
// prints "An animal was initialized with a species of Giraffe"
如果你给该可失败初始化器的 let anonymousCreature = Animal(species: "")
// anonymousCreature is of type Animal?,not Animal
if anonymousCreature == nil {
print("The anonymous creature could not be initialized")
}
// prints "The anonymous creature could not be initialized"
注意:空字符串( 枚举类型的可失败初始化器你可以使用一个可失败初始化器来选择一个合适的带一个或多个形式参数的枚举类型。如果提供的形式参数没有匹配的条件,初始化器可能失败。 下面的??定义一个名为 enum TemperatureUnit {
case Kelvin,Celsius,Fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil
}
}
}
你可以使用可失败初始化器为可能的三种状态来选择一个合适的枚举条件,当参数的值不能与任意一枚举成员相匹配时,该枚举类型的构建过程失败: let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit,so initialization succeeded.")
}
// prints "This is a defined temperature unit,so initialization succeeded."
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit,so initialization failed.")
}
// prints "This is not a defined temperature unit,so initialization failed."
带有原始值的枚举的可失败初始化器带有原始值的枚举会自动获得一个可失败初始化器init?(rawValue:),该可失败初始化器有一个名为 因此上面的 enum TemperatureUnit: Character {
case Kelvin = "K",Celsius = "C",Fahrenheit = "F"
}
let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit,so initialization succeeded.")
}
// prints "This is a defined temperature unit,so initialization succeeded."
let unknownUnit = TemperatureUnit(rawValue: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit,so initialization failed.")
}
// prints "This is not a defined temperature unit,so initialization failed."
类的可失败初始化器一个值类型(结构体或枚举)的可失败初始化器任何时候在它的初始化实现的时候,可以触发初始化失败。在上面 然而,对于类来说,一个可失败的初始化器只能在所有的类属性被初始化后和所有类之间的构造器之间的代理调用发生完后,可以触发初始化失败。 下面的??展示了你如何使用隐式打开一个可选属性来满足来满足可失败初始化器的需要: class Product {
let name: Stirng!
init?(name: String) {
self.name = name
if name.isEmpty { return nil }
}
}
上面的 然而, 上面的??, 因为 f let bowTie = Product(name: "bow tie") {
// no need to check if bowTie.name == nil
print("The product's name is (bowTie.name)")
}
// prints "The product's name is bow tie"
构造失败的传递允许在同一个类,结构体或枚举的可失败初始化器可以横向传递到另一个可失败初始化器。类似地,一个子类可失败初始化器可以向上代理到父类的可失败初始化器。 在其他的情况,如果你代理到另一个初始化器导致了初始化失败,那么整个初始化过程也会立即失败,并且之后任何初始化代码都不会执行。 注意:可失败初始化器也可以代理其他的非可失败初始化器。通过这个方法,你可以为已有的初始化过程添加初始化失败的条件。 下面的??定义了 class CartItem: Product {
let quantity: Int!
init?(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
if quantity < 1 { return nil }
}
}
如果一个父类由于空 如果你创建了一个 if let twoSocks = CartItem(name: "sock",quantity: 2) {
print("Item: (twoSocks.name),quantity: (twoSocks.quantity)")
}
// prints "Item: sock,quantity: 2"
如果你创建了一个 if let zeroShirts = CartItem(name: "shirt",quantity: 0) {
print("Item: (zeroShirts.name), quantity: (zeroShirts.quantity)")
} else {
print("Unable to initialize zero shirts")
}
// prints "Unable to initialize zero shirts"
类似地,如果你创建了一个 if let oneUnnamed = CartItem(name: "",quantity: 1) {
print("Item: (oneUnnamed.name),quantity: (oneUnnamed.quantity)")
} else {
print("Unable to initialize one unnamed product")
}
// prints "Unable to initialize one unnamed product"
重写可失败初始化器你可以在子类里重写父类的可失败初始化器。就好像其他的初始化器。或者,你可以用子类的非可失败初始化器来重写父类可失败初始化器。这样你就可以初始化子类不在失败,尽管父类的初始化允许失败。 注意,如果你用非可失败的子类初始化器重写了一个可失败初始化器时,子类的构造器将不再能向上代理父类的可失败构造器。一个非可失败的构造器永远也不能代理调用一个可失败构造器。 注意:你可以用一个非可失败构造器重写一个可失败构造器,但反过来却行不通。 下面的??定义了一个名为 class Document {
var name: String?
// this initializer creates a document with a nil name value
init() {}
// this initializer creates a document with a non-empty name value
init?(name: String) {
self.name = name
if name.isEmpty { return nil }
}
}
下面这个例子,定义了一个名为 class AutomaticallyNamedDocument: Document {
override init() {
super.init()
self.name = "[Untitled]"
}
override init(name: String) {
super.init()
if name.isEmpty {
self.name = "[Untitled]"
} else {
self.name = name
}
}
}
你可以强制打开一个初始化器来从父类调用一个可失败的初始化器作为子类非可失败初始化器的一部分。例如, class UntitledDocument: Document {
override init() {
super.init(name: "[Untitled]")!
}
}
这种情况,如果父类的 可失败构造器init!通常来说我们通过在 你可以在 必要初始化器在类的构造器前添加 class SomeClass {
required init() {
// initializer implementation goes here
}
}
当子类重写基类的必要构造器时,必须在子类的构造器前同样添加 class SomeSubclass: SomeClass {
required init() {
// subclass implementation of the required initializer goes here
}
}
注意:如果子类继承的构造器能满足必要构造器的需求,则你无需显示的在子类中提供必要构造器的实现。 通过闭包和函数来设置属性的默认值如果某个存储属性的默认值需要自定义或设置,你可以使用闭包( 这种闭包或函数会创建一个和属性相同的临时值,来代表初始的状态,并且把这个临时值作为属性的默认值。 下面的代码展示闭包是如何让提供一个默认的属性值: class SomeClass {
let someProperty: SomeType = {
// create a default value for someProperty inside this closure
// someValue must be of the same type as SomeType
return someValue
}()
}
注意:闭包大括号的结尾有一个没有参数的小括号。这就告诉Swift类立即执行闭包。如果你忽略了这个括号,你就会把闭包作为值分配给了属性,比姑且不会返回闭包的值。 注意:如果你使用了闭包来初始化一个属性,请记住闭包执行的时候,实例的其他部分还没有被初始化。这就意味着你不能在闭包里读取任何其他的属性值,即使这些属性有默认值。你也不能使用隐式 下面的??定义了一个名为 跳棋游戏是在一个黑白交替的10×10的棋盘。为了呈现游戏的棋盘,
struc Checkboard {
let boardColors: []Bool] = {
var temporaryBoard = [Bool]()
var isBlack = false
for i in 1...10 {
for j in 1...10 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
return temporaryBoa
}()
func squareIsBlackAtRow(row: Int,column: Int) ->Bool {
return boardColors[(row * 10) + column]
}
}
当创建了一个新的 let board = Checkerboard()
print(board.squareIsBlackAtRow(0,column: 1))
// prints "true"
print(board.squareIsBlackAtRow(9,column: 9))
// prints "false"
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |