构造器(下)

可失败的构造器

如果一个类,结构体或枚举类型的对象,在构造自身的过程中有可能失败,则为其定义一个可失败构造器,是非常有必要的。这里所指的“失败”是指,如给构造器传入无效的参数值,或缺少某种所需的外部资源,又或是不满足某种必要的条件等。

为了妥善处理这种构造过程中可能会失败的情况。你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在init关键字后面加添问号(init?)

可失败构造器,在构建对象的过程中,创建一个其自身类型为可选类型的对象。你通过return nil 语句,来表明可失败构造器在何种情况下“失败”。

struct Animal {
let species: String
init?(species: String){
if species.isEmpty { return nil }
self.species = species
}
} // 生成someAnimal这个实例,需要注意的是这个实例的类型是可选类型(Animal?)
let someAnimal = Animal(species: "Giraffe") if let giraffe = someAnimal {
print(giraffe.species)
} // 生成一个构造过程会失败的实例
let anonymousCreature = Animal(species: "") if anonymousCreature == nil {
print("anonymousCreature 是空的")
}

枚举类型的可失败构造器

你可以通过构造一个带一个或多个参数的可失败构造器来获取枚举类型中特定的枚举成员。还能在参数不满足你所期望的条件时,导致构造失败。

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 kelvinUnit = TemperatureUnit(symbol: "K") // kelvin
let unknowUnit = TemperatureUnit(symbol: "X") // nil

带原始值的枚举类型的可失败构造器

带原始值的枚举类型会自带一个可失败构造器init?(rawValue:),该可失败构造器有一个名为rawValue的默认参数,其类型和枚举类型的原始值类型一致,如果该参数的值能够和枚举类型成员所带的原始值匹配,则该构造器构造一个带此原始值的枚举成员,否则构造失败。

enum TemperatureUnit: Character {
case Kelvin = "K", Celsius = "C", Fahrenheit = "F"
} let kelvinUnit = TemperatureUnit(rawValue: "K") // kelvin
let unknowUnit = TemperatureUnit(rawValue: "X") // nil

类的可失败构造器

值类型(如结构体或枚举类型)的可失败构造器,对何时何地触发构造失败这个行为没有任何的限制。比如在前面的例子中,结构体Animal的可失败构造器触发失败的行为,甚至发生在species属性的值被初始化以前。而对类而言,就没有那么幸运了。类的可失败构造器只能在所有的类属性被初始化后和所有类之间的构造器之间的代理调用发生完后触发失败行为。

class Product {
let name: String!
init?(name: String) {
self.name = name
// 类的可失败构造器触发失败条件之前,必须确定所有属性都被初始化
if name.isEmpty { return nil }
}
}

构造失败的传递

可失败构造器同样满足在构造器链中所描述的构造规则。其允许在同一类,结构体和枚举中横向代理其他的可失败构造器。类似的,子类的可失败构造器也能向上代理基类的可失败构造器。

无论是向上代理还是横向代理,如果你代理的可失败构造器,在构造过程中触发了构造失败的行为,整个构造过程都将被立即终止,接下来任何的构造代码都将不会被执行。

可失败构造器也可以代理调用其它的非可失败构造器。通过这个方法,你可以为已有的构造过程加入构造失败的条件。

class Product {
let name: String!
init?(name: String) {
self.name = name
if name.isEmpty { return nil }
}
} 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 item = CartItem(name: "shirt", quantity: 0) {
print("Item: \(item.name), quantity: \(item.quantity)")
} else {
print("zero shirt") // 打印出这句
}

重写一个可失败构造器

就如同其它构造器一样,你也可以用子类的可失败构造器重写基类的可失败构造器。或者你也可以用子类的非可失败构造器重写一个基类的可失败构造器。这样做的好处是,即使基类的构造器为可失败构造器,但当子类的构造器在构造过程不可能失败时,我们也可以把它修改过来。

注意当你用一个子类的非可失败构造器重写了一个父类的可失败构造器时,子类的构造器将不再能向上代理父类的可失败构造器。一个非可失败的构造器永远也不能代理调用一个可失败构造器。

注意: 你可以用一个非可失败构造器重写一个可失败构造器,但反过来却行不通。

class Document {
var name: String?
// 这个构造器构造了一个name属性为空的实例
init() {} // 相反 这个构造器构建了name不为空的实例
init?(name: String){
if name.isEmpty { return nil }
self.name = name
}
} class AutomaticallyNamedDocument: Document {
// 重写了父类的构造器,保证name属性永远有值
override init() {
super.init()
name = "[没有命名]"
} // 将父类的可失败构造器重写为普通自定义构造器,保证了name属性永远有值
override init(name: String) {
// 由于将可失败构造器重写为普通自定义构造器,所以这个构造器不能向上代理可失败构造器了
super.init()
if name.isEmpty {
self.name = "[没有命名]"
} else {
self.name = name
}
}
}

可失败构造器 init!

通常来说我们通过在init关键字后添加问号的方式来定义一个可失败构造器,但你也可以使用通过在init后面添加惊叹号的方式来定义一个可失败构造器(init!),该可失败构造器将会构建一个特定类型的隐式解析可选类型的对象。

你可以在 init?构造器中代理调用 init!构造器,反之亦然。 你也可以用 init?重写 init!,反之亦然。 你还可以用 init代理调用init!,但这会触发一个断言:是否 init! 构造器会触发构造失败?

必要构造器

在类的构造器前添加 required 修饰符表明所有该类的子类都必须实现该构造器

注意: 如果子类继承的构造器能满足必要构造器的需求,则你无需显示的在子类中提供必要构造器的实现。

class SomeClass {
required init() {
// 添加必要构造器的实现代码
}
} class SubClass: SomeClass {
// 当子类重写基类的必要构造器时,必须在子类的构造器前同样添加required修饰符以确保当其它类继承该子类时,该构造器同为必要构造器。在重写基类的必要构造器时,不需要添加override修饰符
required init() {
// 添加子类必要构造器的实现代码
}
}

通过闭包和函数来设置属性的默认值

如果某个存储型属性的默认值需要特别的定制或准备,你就可以使用闭包或全局函数来为其属性提供定制的默认值。每当某个属性所属的新类型实例创建时,对应的闭包或函数会被调用,而它们的返回值会当做默认值赋值给这个属性。

这种类型的闭包或函数一般会创建一个跟属性类型相同的临时变量,然后修改它的值以满足预期的初始状态,最后将这个临时变量的值作为属性的默认值进行返回。

class SomeClass {
let list: [String : Double] = {
var scoreList = ["ASK" : 103.5,
"Alice" : 115.8,
"Alisa" : 118.5]
return scoreList
}()
// 注意闭包结尾的大括号后面接了一对空的小括号。这是用来告诉 Swift 需要立刻执行此闭包。如果你忽略了这对括号,相当于是将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性。
}

学习Swift -- 构造器(下)的更多相关文章

  1. 学习Swift -- 构造器(中)

    构造器(中) 值类型的构造器代理 构造器可以通过调用其它构造器来完成实例的部分构造过程.这一过程称为构造器代理,它能减少多个构造器间的代码重复. 构造器代理的实现规则和形式在值类型和类类型中有所不同. ...

  2. 学习Swift -- 构造器(上)

    构造器(上) 构造过程是为了使用某个类.结构体或枚举类型的实例而进行的准备过程.这个过程包含了为实例中的每个存储型属性设置初始值和为其执行必要的准备和初始化任务. 构造过程是通过定义构造器(Initi ...

  3. ios -- 教你如何轻松学习Swift语法(三) 完结篇

    前言:swift语法基础篇(二)来了,想学习swift的朋友可以拿去参考哦,有兴趣可以相互探讨,共同学习哦.      一.自动引用计数   1.自动引用计数工作机制      1.1 swift和o ...

  4. ios -- 教你如何轻松学习Swift语法(二)

    前言:swift语法基础篇(二)来了,想学习swift的朋友可以拿去参考哦,有兴趣可以相互探讨,共同学习哦.      一.可选类型(重点内容)   1.什么是可选类型?        1.1在OC开 ...

  5. ios -- 教你如何轻松学习Swift语法(一)

    目前随着公司开发模式的变更,swift也显得越发重要,相对来说,swift语言更加简洁,严谨.但对于我来说,感觉swift细节的处理很繁琐,可能是还没适应的缘故吧.基本每写一句代码,都要对变量的数据类 ...

  6. 学习swift语言的快速入门教程推荐

    随着苹果产品越来越火爆,苹果新推出的swift必定将在很大程度上代替oc语言.学好swift语言,对于IOS工程师来讲,已经是一门必备技能. 有一些比较好的英文版教程,值得学习. 1. Swift T ...

  7. Swift-如何快速学习Swift

    关于本文: 1.说明本文写作的目的 2.整理了Swift的基本语法树 3.看图作文 一.写作目的 昨天看了一个知识专栏,作者讲述的是“如何研究性的学习”.整个课程1个小时9分钟,花了我19块人民币.其 ...

  8. 学习和研究下unity3d的四元数 Quaternion

    学习和研究下unity3d的四元数 Quaternion 今天准备学习和研究下unity3d的四元数 Quaternion 四元数在电脑图形学中用于表示物体的旋转,在unity中由x,y,z,w 表示 ...

  9. 一步一步学习Swift之(一):关于swift与开发环境配置

    一.什么是Swift? 1.Swift 是一种新的编程语言,用于编写 iOS 和 OS X 应用. 2.Swift 结合了 C 和 Objective-C 的优点并且不受 C 兼容性的限制. 3.Sw ...

随机推荐

  1. 【设计模式 - 22】之策略模式(Strategy)

    1      模式简介 在策略模式中,一个类的行为或其算法可以在运行时改变.策略模式定义了一系列算法,把它们一个个封装起来,并且使它们可以互相替换. 策略模式的优点: 1)        算法可以自由 ...

  2. 修改Unity脚本模板的方法合计

    作为一个习惯于偷懒的程序,重复性的无聊内容是最让人无奈的事,就比如我们创建Unity脚本之后,需要手动调整生成的新脚本的格式.编码.内容:如果我们要编写的是编辑器或者服务器端脚本,需要修改的内容就会更 ...

  3. macos 配置 golang 开发环境

    初次接触golang这门编程语言,一下子就喜欢上了,语法简洁优雅,对于以前有c/c++编程经验的人来说会更加有亲切感. 仅仅学习了一天就能应用beego框架和mogodb数据库开发一个rest api ...

  4. adt-bundle-linux-x86_64-20131030下新建project提示找不到adb和R.java问题的解决

    adt-bundle-linux-x86_64-20131030下新建project提示找不到adb和R.java问题的解决 在ubuntu14.04下,搭建Android开发环境,下载官方的adt- ...

  5. TCP keepalive under Linux

    TCP Keepalive HOWTO Prev   Next 3. Using TCP keepalive under Linux Linux has built-in support for ke ...

  6. CentOS允许/禁止ping的方法

    一.临时生效 1.允许ping >/proc/sys/net/ipv4/icmp_echo_ignore_all 2.禁止ping >/proc/sys/net/ipv4/icmp_ech ...

  7. javascript中的call()和apply应用

    在javascript开发过程中,如果有看过几个javascirpt代码库,就会发现经常使用到call()和apply()函数,call()和aplly()结合javascript允许传递函数名,这种 ...

  8. html通用导航条制作

    第一步:先创建一个盒子,定义类为 nav,width 1000,height 40px,防京东的导航,与浏览器顶部100px,margin-top:100px,看的更直观 第二步:使用无序列表放置,导 ...

  9. PHP ajax实现数组返回

    首先,我想要实这样一个功能, 当选择一个下拉框时,让其它三个文本框得到从服务器上返回的值!也就把返回的值,赋给那三个文本框! 我用的是jquery+php!! 由于我前台,后台,js,数据库采用的都是 ...

  10. [上传下载] C#FileUp文件上传类 (转载)

    点击下载 FileUp.zip 主要功能如下 .把上传的文件转换为字节数组 .流转化为字节数组 .上传文件根据FileUpload控件上传 .把Byte流上传到指定目录并保存为文件 看下面代码吧 // ...