Swift学习:构造器(下)

本篇主要介绍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
    }
}

四、重写一个可失败的构造器

既然是重写,这里主要是应用于类中。这里包括两种情况:

  1. 用子类的可失败构造器重写父类的可失败构造器(这种情况针对于可能子类增加了更多的可失败处理)
  2. 用子类的非可失败构造器重写一个父类的可失败构造器。
//重写一个可失败的构造器一个示例如下:
//父类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!,但是一旦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()

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏python3

python for循环

当range执行完之后,代码执行else部分代码。如果遇到break,终止循环,不会走else代码

33210
来自专栏AI研习社

最常见的 35 个 Python 面试题及答案(2018 版)

作为一个 Python 新手,你必须熟悉基础知识。在本文中我们将讨论一些 Python 面试的基础问题和高级问题以及答案,以帮助你完成面试。包括 Python ...

87130
来自专栏全沾开发(huā)

搞懂JavaScript中的连续赋值

搞懂JavaScript中的连续赋值 前段时间老是被一道题刷屏,一个关于连续赋值的坑。 遂留下一个笔记,以后再碰到有人问这个题,直接...

41360
来自专栏WD学习记录

数据结构与算法2016-06-03

一个算法调用自己来完成它的部分工作,在解决某些问题时,一个算法需要调用自身。如果一个算法直接调用自己或间接调用自己,就称这个算法是递归的。根据调用方式的不同,它...

8520
来自专栏古时的风筝

模板的简单介绍与使用

什么是模板? 模板(template)指c++中的函数模板与类模板,大体对应于C#和Java众的泛型的概念。目前,模板已经成为C++的泛型编程中不可缺少的一部分...

19180
来自专栏JavaEdge

青铜到王者 ,快速提升你 Go语言的段位! "狗"语言实战(二)- 基础语法1 变量定义

14840
来自专栏猿人谷

Java初学者需掌握的30个概念

基本概念:       1.OOP中唯一关心的是对象的接口是什么,就像计算机的销售商她不管电源内部结构 是怎样的,他只关系能否给你提供电就行了,也就是只要知道c...

180100
来自专栏JetpropelledSnake

Python入门之迭代器/生成器/yield的表达方式/面向过程编程

 本章内容     迭代器     面向过程编程       一、什么是迭代       二、什么是迭代器       三、迭代器演示和举例       四、生...

30690
来自专栏信安之路

php 弱类型问题

php 是一门简单而强大的语言,提供了很多 Web 适用的语言特性,其中就包括了变量弱类型,在弱类型机制下,你能够给一个变量赋任意类型的值。

17000
来自专栏蜕变

Python 数据类型

Python主要数据类型包括list(列表)、tuple(元组)、dict(字典)和set(集合)等对象,下面逐一介绍这些Python数据类型。

9000

扫码关注云+社区

领取腾讯云代金券