// 这章比较重要
//构造
//“构造过程是使用类、结构体或枚举类型的实例之前的准备过程。在新实例可用前必须执行这个过程,具体操作包括设置实例中每个存储型属性的初始值和执行其他必须的设置或初始化工作。”
//“通过定义构造器来实现构造过程,这些构造器可以看做是用来创建特定类型新实例的特殊方法。与 Objective-C 中的构造器不同,Swift 的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化”
//“类的实例也可以通过定义析构器在实例释放之前执行特定的清除工作"
//1. 存储类型的初始赋值
//“类和结构体在创建实例时,必须为所有存储型属性设置合适的初始值。存储型属性的值不能处于一个未知的状态。”
//“你可以在构造器中为存储型属性赋初值,也可以在定义属性时为其设置默认值”
//“注意:当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观察者。”
//构造器
//“构造器在创建某个特定类型的新实例时被调用。它的最简形式类似于一个不带任何参数的实例方法,以关键字init命名:”
/*
init(){
//在此处执行构造过程
}
*/
struct Fahrenheit{
var temperature : Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
print("the default temperature is \(f.temperature) ")
//默认属性值
//“你可以在构造器中为存储型属性设置初始值。同样,你也可以在属性声明时为其设置默认值。”
//“注意: 如果一个属性总是使用相同的初始值,那么为其设置一个默认值比每次都在构造器中赋值要好。两种方法的效果是一样的,只不过使用默认值让属性的初始化和声明结合得更紧密。”
struct FFFFFF {
var temperature = 32.0
}
//2. 自定义构造过程
//“你可以通过输入参数和可选类型的属性来自定义构造过程,也可以在构造过程中修改常量属性”
//2.1构造参数
//“自定义构造过程时,可以在定义中提供构造参数,指定所需值的类型和名字。构造参数的功能和语法跟函数和方法的参数相同。”
struct Celsius{
var temperatureInCelsius : Double
init(fromFahrenheit fahrenheit:Double) {
temperatureInCelsius = (fahrenheit - 32.0)/1.8
}
init(fromKelvin kelvin:Double) {
temperatureInCelsius = kelvin - 273.15
}
}
let boiling = Celsius(fromFahrenheit:212.0)
boiling.temperatureInCelsius
let freezingPointOfWater = Celsius(fromKelvin:273.15)
freezingPointOfWater.temperatureInCelsius
//“第一个构造器拥有一个构造参数,其外部名字为fromFahrenheit,内部名字为fahrenheit;第二个构造器也拥有一个构造参数,其外部名字为fromKelvin,内部名字为kelvin。这两个构造器都将唯一的参数值转换成摄氏温度值,并保存在属性temperatureInCelsius中。”
//2.2 参数内部名称和外部名称
//“跟函数和方法参数相同,构造参数也拥有一个在构造器内部使用的参数名字和一个在调用构造器时使用的外部参数名字”
//“然而,构造器并不像函数和方法那样在括号前有一个可辨别的名字。因此在调用构造器时,主要通过构造器中的参数名和类型来确定应该被调用的构造器。正因为参数如此重要,如果你在定义构造器时没有提供参数的外部名字,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)
//let veryGreen = Color(0.0,1.0,0.0) 编译报错
//2.3 不带外部名的构造器参数
//“如果你不希望为构造器的某个参数提供外部名字,你可以使用下划线(_)来显式描述它的外部名”
struct Jusus{
var name :String
init(getName name:String) {
self.name = name
}
init(_ name:String) {
self.name = name
}
}
let jusus = Jusus(getName:"jack")
let anotherJusus = Jusus("rose")
//2.4 可选属性类型
// “如果你定制的类型包含一个逻辑上允许取值为空的存储型属性——无论是因为它无法在初始化时赋值,还是因为它在之后某个时间点可以赋值为空——你都需要将它定义为可选类型”
//“可选类型的属性将自动初始化为nil,表示这个属性是有意在初始化时设置为空的。”
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()
//打印 Do you like cheese?
cheeseQuestion.response = "Yes I do like cheese"
//“调查问题的答案在回答前是无法确定的,因此我们将属性response声明为String?类型,或者说是可选字符串类型。当SurveyQuestion实例化时,它将自动赋值为nil,表明此字符串暂时还没有值。”
//2.5 构造过程中常量属性的修改
//“你可以在构造过程中的任意时间点给常量属性指定一个值,只要在构造过程结束时是一个确定的值。一旦常量属性被赋值,它将永远不可更改”
//“注意 对于类的实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。”
class SurveyQuestionAgain{
let text : String
var response : String?
init(text : String) {
self.text = text
}
func ask() {
print(text)
}
}
let beetsQuestion = SurveyQuestionAgain(text: "how about beets?")
beetsQuestion.ask()
//3. 默认构造器
//“如果结构体或类的所有属性都有默认值,同时没有自定义的构造器,那么 Swift 会给这些结构体或类提供一个默认构造器(default initializers)。这个默认构造器将简单地创建一个所有属性值都设置为默认值的实例。”
class ShoppingList{
var name : String?
var quantity = 1
var purchased = false
}
var item = ShoppingList()
//“类中的所有属性都有默认值,且它是没有父类的基类,它将自动获得一个可以为所有属性设置默认值的默认构造器(尽管代码中没有显式为name属性设置默认值,但由于name是可选字符串类型,它将默认设置为nil)”
//3.2 结构体的逐一城阳构造器
//“除了上面提到的默认构造器,如果结构体没有提供自定义的构造器,它们将自动获得一个逐一成员构造器,即使结构体的存储型属性没有默认值。”
//“逐一成员构造器是用来初始化结构体新实例里成员属性的快捷方法。我们在调用逐一成员构造器时,通过与成员属性名相同的参数名进行传值来完成对成员属性的初始赋值。”
struct SizeA {
var width = 0.0 ,height = 0.0
}
let twoByTwo = SizeA(width:2.0,height:1.0)
//4. 值类型的构造器代理
//“构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能减少多个构造器间的代码重复”
//“构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给自己的其它构造器,类则不同,它可以继承自其它类(请参考继承),这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化”
//“对于值类型,你可以使用self.init在自定义的构造器中引用相同类型中的其它构造器。并且你只能在构造器内部调用self.init。”
//“如果你为某个值类型定义了一个自定义的构造器,你将无法访问到默认构造器(如果是结构体,还将无法访问逐一成员构造器)。这种限制可以防止你为值类型增加了一个额外的且十分复杂的构造器之后,仍然有人错误的使用自动生成的构造器”
//“注意 假如你希望默认构造器、逐一成员构造器以及你自己的自定义构造器都能用来创建实例,可以将自定义的构造器写到扩展(extension)中,而不是写在值类型的原始定义中”
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)
}
}
//“第一个Rect构造器init(),在功能上跟没有自定义构造器时自动获得的默认构造器是一样的。这个构造器是一个空函数,使用一对大括号{}来表示,它没有执行任何构造过程。调用这个构造器将返回一个Rect实例,它的origin和size属性都使用定义时的默认值Point(x: 0.0, y: 0.0)和Size(width: 0.0, height: 0.0):”
let basicRect = Rect()
//“basicRect 的 origin 是 (0.0, 0.0),size 是 (0.0, 0.0)”
//“第二个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 的 origin 是 (2.0, 2.0),size 是 (5.0, 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))
//“centerRect 的 origin 是 (2.5, 2.5),size 是 (3.0, 3.0)
//5. 类的继承和构造过程
// “类里面的所有存储型属性——包括所有继承自父类的属性——都必须在构造过程中设置初始值。”
//“Swift 为类类型提供了两种构造器来确保实例中所有存储型属性都能获得初始值,它们分别是指定构造器和便利构造器”
//5.1 指定构造器和便利构造器
//“指定构造器是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。”
// “每一个类都必须拥有至少一个指定构造器。在某些情况下,许多类通过继承了父类中的指定构造器而满足了这个条件”
//“便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入值的实例。”
//“你应当只在必要的时候为类提供便利构造器”
//5.2 指定构造器和便利构造器语法
/*
指定构造器
init(parameters){
statements
}
便利构造器
convenience init(parameters){
statements
}
*/
//5.3 类的构造器代理规则
//“规则 1 指定构造器必须调用其直接父类的的指定构造器。”
//“规则 2 便利构造器必须调用同类中定义的其它构造器。”
//“规则 3 便利构造器必须最终导致一个指定构造器被调用。”
//“一个更方便记忆的方法是: 指定构造器必须总是向上代理,便利构造器必须总是横向代理
//备注: 可以去看书上的图片
//5.4 两段式构造过程
//“Swift 中类的构造过程包含两个阶段。第一个阶段,每个存储型属性被引入它们的类指定一个初始值。当每个存储型属性的初始值被确定后,第二阶段开始,它给每个类一次机会,在新实例准备使用之前进一步定制它们的存储型属性。”
//“Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程能不出错地完成:”
//“安全检查1 指定构造器必须保证它所在类引入的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中的构造器”
//“安全检查2 指定构造器必须先向上代理调用父类构造器,然后再为继承的属性设置新值。如果没这么做,指定构造器赋予的新值将被父类中的构造器所覆盖。”
//“安全检查3 便利构造器必须先代理调用同一类中的其它构造器,然后再为任意属性赋新值。如果没这么做,便利构造器赋予的新值将被同一类中其它指定构造器所覆盖。”
//“安全检查4 构造器在第一阶段构造完成之前,不能调用任何实例方法,不能读取任何实例属性的值,不能引用self作为一个值。”
//阶段一:
/*
某个指定构造器或便利构造器被调用。”
完成新实例内存的分配,但此时内存还没有被初始化。
指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化。
指定构造器将调用父类的构造器,完成父类属性的初始化。
这个调用父类构造器的过程沿着构造器链一直往上执行,直到到达构造器链的最顶部。
当到达了构造器链最顶部,且已确保所有实例包含的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化。此时阶段 1 完成。”
/
//阶段二:
/
“从顶部构造器链一直往下,每个构造器链中类的指定构造器都有机会进一步定制实例。构造器此时可以访问self、修改它的属性并调用实例方法等等。
最终,任意构造器链中的便利构造器可以有机会定制实例和使用self。”
*/
// 备注:这部分书上也有图。
//5.5 构造器的继承和重写
//“跟 Objective-C 中的子类不同,Swift 中的子类默认情况下不会继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更精细的子类继承,并被错误地用来创建子类的实例。”
//“注意 父类的构造器仅会在安全和适当的情况下被继承。具体内容请参考后续章节构造器的自动继承。”
//“当你在编写一个和父类中指定构造器相匹配的子类构造器时,你实际上是在重写父类的这个指定构造器。因此,你必须在定义子类构造器时带上override修饰符。即使你重写的是系统自动提供的默认构造器,也需要带上override修饰符”
//“如果你编写了一个和父类便利构造器相匹配的子类构造器,由于子类不能直接调用父类的便利构造器(每个规则都在上文类的构造器代理规则有所描述),因此,严格意义上来讲,你的子类并未对一个父类构造器提供重写。最后的结果就是,你在子类中“重写”一个父类便利构造器时,不需要加override前缀”
class Vehicle{
var numberOfWheels = 0
var description :String{
return "\(numberOfWheels) wheels"
}
}
//“Vehicle类只为存储型属性提供默认值,而不自定义构造器。因此,它会自动获得一个默认构造器。自动获得的默认构造器总会是类中的指定构造器,它可以用于创建numberOfWheels为0的Vehicle实例”
let vehicle = Vehicle()
print("Vehicle: \(vehicle.description)")
//打印 Vehicle: 0 wheels
class Bicycle:Vehicle{
override init() {
super.init()
numberOfWheels = 2
}
}
let bicycle = Bicycle()
print("bicycle:\(bicycle.description)")
//打印 bicycle:2 wheels
//“子类Bicycle定义了一个自定义指定构造器init()。这个指定构造器和父类的指定构造器相匹配,所以Bicycle中的指定构造器需要带上override修饰符。”
//“Bicycle的构造器init()以调用super.init()方法开始,这个方法的作用是调用Bicycle的父类Vehicle的默认构造器。这样可以确保Bicycle在修改属性之前,它所继承的属性numberOfWheels能被Vehicle类初始化。在调用super.init()之后,属性numberOfWheels的原值被新值2替换。”
//“注意 子类可以在初始化时修改继承来的变量属性,但是不能修改继承来的常量属性”
//5.6 构造器的自动继承
//“子类在默认情况下不会继承父类的构造器。但是如果满足特定条件,父类构造器是可以被自动继承的。在实践中,这意味着对于许多常见场景你不必重写父类的构造器,并且可以在安全的情况下以最小的代价继承父类的构造器”
//“假设你为子类中引入的所有新属性都提供了默认值,以下 2 个规则适用”
/*
“规则 1
如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。
规则 2
如果子类提供了所有父类指定构造器的实现——无论是通过规则 1 继承过来的,还是提供了自定义实现——它将自动继承所有父类的便利构造器。”
*/
/*解析: 子类新属性都提供了默认值的情况下,
没有定义任何指定构造器: 自动继承父类指定+便利构造器
子类自定义自己的构造器,重写了部分或者没有重写父类指定构造器: 不继承父类指定+便利构造器
子类自定义了自己的构造器,全部重写了父类所有的指定构造器: 继承父类的便利构造器
子类没有自定义自己的构造器,全部重写了父类的指定构造器: 继承父类 便利构造器
*/
//5.7 指定构造器和便利构造器实践
class Food{
var name:String
init(name:String) {
self.name = name
}
convenience init(){
self.init(name: "[Unnamed]")
}
}
let namedMeat = Food(name:"Bacon")
//“namedMeat 的名字是 "Bacon”
//“Food类中的构造器init(name: String)被定义为一个指定构造器,因为它能确保Food实例的所有存储型属性都被初始化。Food类没有父类,所以init(name: String)构造器不需要调用super.init()来完成构造过程。”
//“Food类同样提供了一个没有参数的便利构造器init()。这个init()构造器为新食物提供了一个默认的占位名字,通过横向代理到指定构造器init(name: String)并给参数name传值[Unnamed]来实现”
let mysteryMeat = Food()
//“mysteryMeat 的名字是 [Unnamed]”
class Recipegredient: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实例的所有属性值。这个构造器一开始先将传入的quantity参数赋值给quantity属性,这个属性也是唯一在RecipeIngredient中新引入的属性。随后,构造器向上代理到父类Food的init(name: String)”
//“RecipeIngredient还定义了一个便利构造器init(name: String),它只通过name来创建RecipeIngredient的实例。这个便利构造器假设任意RecipeIngredient实例的quantity为1,所以不需要显式指明数量即可创“建出实例。这个便利构造器的定义可以更加方便和快捷地创建实例,并且避免了创建多个quantity为1的RecipeIngredient实例时的代码重复。这个便利构造器只是简单地横向代理到类中的指定构造器,并为quantity参数传递1”
//“注意,RecipeIngredient的便利构造器init(name: String)使用了跟Food中指定构造器init(name: String)相同的参数。由于这个便利构造器重写了父类的指定构造器init(name: String),因此必须在前面使用override修饰符”
//“在这个例子中,RecipeIngredient的父类是Food,它有一个便利构造器init()。这个便利构造器会被RecipeIngredient继承。这个继承版本的init()在功能上跟Food提供的版本是一样的,只是它会代理到RecipeIngredient版本的init(name: String)而不是Food提供的版本。”
let oneMysteyItem = Recipegredient()
let oneBacon = Recipegredient(name:"a bacon")
let sixEgg = Recipegredient(name:"eggs",quantity:6)
class ShoppingListItems:Recipegredient{
var purchased = false
var description:String {
var output = "\(quantity) x \(name) "
output += purchased ? "yes":"no"
return output
}
}
//“由于它为自己引入的所有属性都提供了默认值,并且自己没有定义任何构造器,ShoppingListItem将自动继承所有父类中的指定构造器和便利构造器。”
//“你可以使用全部三个继承来的构造器来创建ShoppingListItem的新实例”
var breakfastList = [ShoppingListItems(),
ShoppingListItems(name:"bacon"),
ShoppingListItems(name:"backon",quantity:5)]
breakfastList[0].name = "Orange juice"
breakfastList[0].purchased = true
for item in breakfastList {
print(item.description)
}
//打印
/*
1 x Orange juice yes
1 x bacon no
5 x backon no
*/
//6. 可失败构造器
//“如果一个类、结构体或枚举类型的对象,在构造过程中有可能失败,则为其定义一个可失败构造器。这里所指的“失败”是指,如给构造器传入无效的参数值,或缺少某种所需的外部资源,又或是不满足某种必要的条件等。”
//“为了妥善处理这种构造过程中可能会失败的情况。你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在init关键字后面添加问号(init?)”
//“可失败构造器会创建一个类型为自身类型的可选类型的对象。你通过return nil语句来表明可失败构造器在何种情况下应该“失败”。”
//“注意 严格来说,构造器都不支持返回值。因为构造器本身的作用,只是为了确保对象能被正确构造。因此你只是用return nil表明可失败构造器构造失败,而不要用关键字return来表明构造成功。”
struct Animal{
let species:String
init?(species:String) {
if species.isEmpty {return nil}
self.species = species
}
}
let someCreature = Animal(species:"Giraffe")
//someCreature 的类型是 Animal? 而不是Animal
if let giraffe = someCreature {
print("An animal was initiliazed with a species of \(giraffe.species)")
}
//打印 An animal was initiliazed with a species of Giraffe
let anonymousCreat = Animal(species:"")
if anonymousCreat == nil{
print("the anonymousCreat could not be initialized")
}
//打印 the anonymousCreat could not be initialized
//6.2 枚举类型的可失败构造器
//“你可以通过一个带一个或多个参数的可失败构造器来获取枚举类型中特定的枚举成员。如果提供的参数无法匹配任何枚举成员,则构造失败。”
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 fathrenheitUnit = TemperatureUnit(symbol:"F")
if fathrenheitUnit != nil{
print("this is adefined tempereature unit,so initialization successed")
}
//打印 this is adefined tempereature unit,so initialization successed
let unknownUnit = TemperatureUnit(symbol:"X")
if unknownUnit == nil {
print("this is not a defined temperature unit so initialization failed")
}
//打印 this is not a defined temperature unit so initialization failed
//6.3 带原始值的枚举类型的可失败构造器
//“带原始值的枚举类型会自带一个可失败构造器init?(rawValue:),该可失败构造器有一个名为rawValue的参数,其类型和枚举类型的原始值类型一致,如果该参数的值能够和某个枚举成员的原始值匹配,则该构造器会构造相应的枚举成员,否则构造失败。”
enum SomeEnumUnit:Character{
case Kelvin = "K",Celsius = "C", Fahrenheit = "F"
}
let fUnit = SomeEnumUnit(rawValue:"F")
if fUnit != nil{
print("f is initialization successed")
}
//打印 f is initialization successed
let unknowU = SomeEnumUnit(rawValue:"X")
if unknowU == nil{
print("this is not a defined")
}
//打印this is not a defined
//6.4 构造失败的传递
//“类,结构体,枚举的可失败构造器可以横向代理到类型中的其他可失败构造器。类似的,子类的可失败构造器也能向上代理到父类的可失败构造器。”
// “无论是向上代理还是横向代理,如果你代理到的其他可失败构造器触发构造失败,整个构造过程将立即终止,接下来的任何构造代码不会再被执行”
//注意: “可失败构造器也可以代理到其它的非可失败构造器。通过这种方式,你可以增加一个可能的失败状态到现有的构造过程中”
class Product {
let name:String
init?(name:String) {
if name.isEmpty {return nil}
self.name = name
}
}
class CartItem:Product{
let quantity:Int
init?(name: String,quantity:Int) {
if quantity<1 {
return nil
}
self.quantity = quantity
super.init(name: name)
}
}
//“倘若你以一个值为 0 的 quantity 来创建一个 CartItem 实例,那么将导致 CartItem 构造器失败”
//“传入一个值为空字符串的 name来创建一个 CartItem 实例,那么将导致父类 Product 的构造过程失败”
//6.5 重写一个可失败的构造器
//“如同其它的构造器,你可以在子类中重写父类的可失败构造器。或者你也可以用子类的非可失败构造器重写一个父类的可失败构造器。这使你可以定义一个不会构造失败的子类,即使父类的构造器允许构造失败”
//“注意,当你用子类的非可失败构造器重写父类的可失败构造器时,向上代理到父类的可失败构造器的唯一方式是对父类的可失败构造器的返回值进行强制解包”
//“注意,你可以用非可失败构造器重写可失败构造器,但反过来却不行”
class Document {
var name:String?
//该构造器创建了一个name属性是nil的document实例
init() {}
//该构造器创建了一个name属性的值为非空字符串的document实例
init?(name:String) {
if name.isEmpty {
return nil
}
self.name = name
}
}
class AutomaticllyNamedDocument:Document{
override init() {
super.init()
self.name = "[Unittled]";
}
override init(name: String) {
super.init()
if name.isEmpty{
self.name = "[Untitled]"
}else{
self.name = name
}
}
}
//“AutomaticallyNamedDocument用一个非可失败构造器init(name:)重写了父类的可失败构造器init?(name:)。因为子类用另一种方式处理了空字符串的情况,所以不再需要一个可失败构造器,因此子类用一个非可失败构造器代替了父类的可失败构造器。”
//“你可以在子类的非可失败构造器中使用强制解包来调用父类的可失败构造器”
class untitledDocument:Document{
override init() {
super.init(name: "[Untitled]")!
}
}
//“如果在调用父类的可失败构造器init?(name:)时传入的是空字符串,那么强制解包操作会引发运行时错误。不过,因为这里是通过非空的字符串常量来调用它,所以并不会发生运行时错误。”
//6.6 可失败构造器init!
//“通常来说我们通过在init关键字后添加问号的方式(init?)来定义一个可失败构造器,但你也可以通过在init后面添加惊叹号的方式来定义一个可失败构造器(init!),该可失败构造器将会构建一个对应类型的隐式解包可选类型的对象。”
//“你可以在init?中代理到init!,反之亦然。你也可以用init?重写init!,反之亦然。你还可以用init代理到init!,不过,一旦init!构造失败,则会触发一个断言”
//7. 必要构造器
//“在类的构造器前添加required修饰符表明所有该类的子类都必须实现该构造器”
// “在子类重写父类的必要构造器时,必须在子类的构造器前也添加required修饰符,表明该构造器要求也应用于继承链后面的子类。在重写父类中必要的指定构造器时,不需要添加override修饰符”
//“注意 如果子类继承的构造器能满足必要构造器的要求,则无须在子类中显式提供必要构造器的实现”
//8. 通过闭包或者函数设置属性的默认值
//“如果某个存储型属性的默认值需要一些定制或设置,你可以使用闭包或全局函数为其提供定制的默认值。每当某个属性所在类型的新实例被创建时,对应的闭包或函数会被调用,而它们的返回值会当做默认值赋值给这个属性”
//“这种类型的闭包或函数通常会创建一个跟属性类型相同的临时变量,然后修改它的值以满足预期的初始状态,最后返回这个临时变量,作为属性的默认值”
/*
class SomeClass {
let someProperty: SomeType = {
// 在这个闭包中给 someProperty 创建一个默认值
// someValue 必须和 SomeType 类型相同
return someValue
}()
}
*/
//“注意闭包结尾的大括号后面接了一对空的小括号。这用来告诉 Swift 立即执行此闭包。如果你忽略了这对括号,相当于将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性”
//“注意,如果你使用闭包来初始化属性,请记住在闭包执行时,实例的其它部分都还没有初始化。这意味着你不能在闭包里访问其它属性,即使这些属性有默认值。同样,你也不能使用隐式的self属性,或者调用任何实例方法。”
struct Checkboard{
let boardColors : [Bool] = {
var temporaryBoard = [Bool]()
var isBlock = false
for i in 1...8 {
for j in 1...8 {
temporaryBoard.append(isBlock)
isBlock = !isBlock
}
isBlock = !isBlock
}
return temporaryBoard
}()
func squareisBlackAtRow(row:Int,column:Int) -> Bool {
return boardColors[(row*8)+column]
}
}
let board = Checkboard()
print(board.boardColors)
print(board.squareisBlackAtRow(row: 0, column: 1))
print(board.squareisBlackAtRow(row: 7, column: 7))