本篇主要介绍Swift中构造器的一些特殊用法
顾名思义,这是用于我们构造过程可能失败情况的构造器。失败的原因可能是给构造器传入无效的参数值,或缺少某种所需的外部资源,又或是不满足某种必要的条件等。
//一个可失败构造器示例如下:
struct Animal {
let name :String
//可失败构造器语法是init关键字后面添加问号即(init?)
init?(name:String){
if name.isEmpty {
//其实Swift构造器并不支持返回值,因为构造器本来就是要确保对象能被正确构造。
//所以这里使用return nil只是为了表明可失败构造器构造失败。
return nil
}
//参数不为空,继续构造器得到可用的实例
self.name = name
}
//注意:
//1.可失败构造器的参数名和参数类型,不能与其它非可失败构造器的参数名,及其参数类型相同。
//2.可失败构造器其实是在构造失败时创建一个类型为自身类型的可选类型的对象。
}
通过枚举类型可失败构造器获取枚举类型中特定的枚举成员,完成构造任务。如果提供的参数无法匹配任何枚举成员则构造失败。
//使用示例如下:
enum TestNum{
case First, Second, Third
init?(number : Character){
switch number{
case "1":
self = .First
case "2":
self = .Second
case "3":
self = .Third
default:
return nil
}
}
}```
不同于以上的写法,swift中还可以使用带原始值的枚举类型可失败构造器。
带原始值的枚举类型会自带一个可失败构造器init?(rawValue:),这里名为rawValue的参数,其类型和枚举类型的原始值类型一致,
如果该参数的值能够和某个枚举成员的原始值匹配,则该构造器会构造相应的枚举成员,否则构造失败。
//这种写法如下: enum TestNum2:Character{ //枚举值自带原始值 case First = "1", Second = "2", Third = "3" }
let number = TestNum2(rawValue: "6") if number == nil{ print("枚举类型构造失败")//此句被打印 }```
与普通的构造器相似,可失败构造器也是可以代理的。这里包括类、结构体、枚举中的横向代理,也包括子类和父类之间的向上代理。 1.无论是向上代理还是横向代理,如果你代理到的其他可失败构造器触发构造失败,整个构造过程将立即终止,接下来的任何构造代码不会再被执行。 2.可失败构造器也可以代理到其它的非可失败构造器。通过这种方式,你可以增加一个可能的失败状态到现有的构造过程中。
class Product {
var name: String!
init?(name: String) {
if name.isEmpty {
return nil
}
self.name = name
}
}
class CartItem: Product {
var quantity: Int!
init?(name: String, quantity: Int) {
if (quantity < 1) {
//验证quantity不符合要求,就立刻终止构造器,返回nil对象,剩余代码也不再执行
return nil;
}
super.init(name: name)
self.quantity = quantity
}
}
既然是重写,这里主要是应用于类中。这里包括两种情况:
//重写一个可失败的构造器一个示例如下:
//父类Person:要求其name属性必须是非空字符串或者nil
class Person {
var name:String?
//该构造器可创建name属性是nil的Person实例
init(){}
//父类的可失败构造器,当传入参数为空的时候,不能创建有效实例
init?(name :String){
self.name = name
if name.isEmpty{
return nil
}
}
}
//子类Student:
class Student:Person {
override init(){
super.init()
self.name = "匿名"
}
//子类的非可失败构造器init(name:)重写了父类的可失败构造器init?(name:)。
//因为这里很好的处理了空字符串的情况,无论传入参数是否是空字符串,都将创建有效实例
//注意:可以用非可失败构造器重写可失败构造器,但反过来却不行。
override init(name: String) {
super.init()
if name.isEmpty{
self.name = "匿名"
}else{
self.name = name
}
}
}
//子类:Teacher:
//我们也可以在子类的非可失败构造器中使用强制解包来调用父类的可失败构造器,具体使用如下:
class Teacher:Person{
override init() {
super.init(name: "匿名")!
}
//这里子类的非可失败构造器重写父类的可失败构造器
//向上代理到父类的可失败构造器,并对父类的可失败构造器的返回值进行强制解包
override init(name: String) {
if name.isEmpty{
super.init(name: "匿名")!
}else{
super.init(name: name)!
}
}
}
区别于init?方式的可失败构造器,init!形式的可失败构造器器将创建一个对应类型的隐式解包可选型对象 init?和init!可以相互代理,也可以相互重写。我们也可以用init代理到init!,但是一旦init!构造失败,将触发断言
在类的构造器前添加required修饰符表明所有该类的子类都必须实现该构造器。
class SuperClass {
required init() {
// 构造器的实现代码
}
}
class SubClass: SuperClass {
//1.子类重写父类的必要构造器,构造器前也必须添加required修饰符,表明该构造器要求也应用于继承链后面的子类。
//2.但是这里虽然重写父类中必要的指定构造器时,却不需要添加override修饰符:
required init() {
//构造器的实现代码
}
}
在构造过程中,我们可以使用闭包或全局函数为某个存储型属性提供定制的默认值。 在新实例被创建时,对应的闭包或函数会被调用,其返回值会当做默认值赋值给这个属性, 具体的时候用如下:
struct Weather {
//常见的一些天气
let commonWeather: [String] = {
var weather = ["sunny", "cloud","rain","snow"];
return weather
}()
//打印
func printCommonWeather(){
for i in 0 ..< commonWeather.count {
print(commonWeather[i])
}
}
}
let weather = Weather()//同时初始化了默认属性
weather.printCommonWeather()