zoukankan      html  css  js  c++  java
  • 从0开始学Swift笔记整理(三)

    这是跟在上一篇博文后续内容:

    ——Swift中相关的属性

    存储属性

    Swift中的属性分为存储属性和计算属性,存储属性就是Objective-C中的数据成员,计算属性不存储数据,但可以通过计算其他属性返回数据。存储属性可以存储数据,分为常量属性(用关键字let定义)和变量属性(用关键var定义)。
    存储属性概念:
    我们在前面曾用到过属性,Employee类和Department结构体。它们的类图如下,Employee 的部门属性dept与Department之间进行了关联。 

     

    我们可以在定义存储属性时指定默认值,示例代码如下:
    class Employee {
        let no: Int = 0
        var name: String = ""
        var job: String?
        var salary: Double = 0
        var dept: Department?
    }
    struct Department {
        let no: Int = 0
        var name: String = ""
    }
    let emp = Employee()
    emp.no = 100 //编译错误:修改常量属性,程序会发生编译错误
    let dept = Department()
    dept.name = "SALES" //编译错误:dept是值类型,值类型不能修改,即便它的属性name是变量属性,也不能修改
    let emp1 = Employee()
    emp1.name =  "Tony"

    计算属性

    计算属性本身不存储数据,而是从其他存储属性中计算得到数据。

    计算属性提供了一个getter(取值访问器)来获取值,以及一个可选的setter(设置访问器)来间接设置其他属性或变量的值。计算属性的语法格式如下:

    面向对象类型 类型名 {

    存储属性

    ......

    var 计算属性名: 属性数据类型 {

    get {

    return 计算后属性值

    }

    set (新属性值) {

    ......

    }

    }

    }

    定义计算属性比较麻烦,要注意后面的几个大括号的对齐关系。

    我们先看一个示例:

    import Foundation

    class Employee {

    var no: Int = 0

    var firstName: String = "Tony" //存储属性

    var lastName: String = "Guan" //存储属性

    var job: String?

    var salary: Double = 0

    lazy var dept: Department = Department()

    var fullName: String { //计算属性

    get {

    return firstName + "." + lastName //返回拼接的结果

    }

    set (newFullName) { //存储传递进来的参数值

    var name = newFullName.componentsSeparatedByString(".")

    firstName = name[0]

    lastName = name[1]

    }

    }

    }

    struct Department {

    let no: Int = 0

    var name: String = ""

    }

    var emp = Employee()

    print(emp.fullName) //取出属性值

    emp.fullName = "Tom.Guan" //给属性赋值

    print(emp.fullName)

    只读计算属性:

    计算属性可以只有getter访问器,没有setter访问器,这就是只读计算属性。指定计算属性不仅不用写setter访问器,而且get{}代码也可以省略。与上一节相比,代码将大大减少。修改上一节示例为只读计算属性,代码如下:

    class Employee {

    var no: Int = 0

    var firstName: String = "Tony"

    var lastName: String = "Guan"

    var job: String?

    var salary: Double = 0

    lazy var dept: Department = Department()

    var fullName: String { //简洁的setter访问器

    return firstName + "." + lastName

    }

    }

    struct Department {

    let no: Int = 0

    var name: String = ""

    }

    var emp = Employee()

    print(emp.fullName)

    只读计算属性不能够赋值,下列语句是错误的。

    emp.fullName = "Tom.Guan"

    属性观察者

    为了监听属性的变化,Swift提供了属性观察者。属性观察者能够监听存储属性的变化,即便变化前后的值相同,它们也能监听到。
    属性观察者主要有以下两个:
    · willSet:观察者在修改之前调用。
    · didSet:观察者在修改之后立刻调用。
    属性观察者的语法格式如下:
    面向对象类型 类型名 {     
        ...
        var 存储属性: 属性数据类型 = 初始化值 {
            willSet(新值) {   //定义willSet观察者。“新值”是传递给willSet观察者的参数,它保存了将要替换原来属性的新值
            ...
            }        
            didSet(旧值) {  //定义didSet观察者。“旧值”是传递给didSet观察者的参数,它保存了被新属性替换的旧值。
            ...
            }       
       }       

    属性观察者的语法格式比计算属性要混乱。
    属性观察者可以在类和结构体中使用,不能在枚举中使用。
    示例代码如下:
    class Employee { 
        var no: Int = 0
        var name: String = "Tony" { 
            willSet(newNameValue) { //定义name属性的willSet观察者,newNameValue是由我们分配的传递新值的参数名
            print("员工name新值:(newNameValue)")  
            }
            didSet(oldNameValue) { //定义name属性的didSet观察者,oldNameValue是由我们分配的传递旧值的参数名
            print("员工name旧值:(oldNameValue)")
            }
       }
       var job: String?
       var salary: Double = 0
       var dept: Department?
    }

    struct Department {  
        var no: Int = 10 {  
            willSet {    //定义no属性的willSet观察者,注意这里没有声明参数,但是我们可以在观察者内部使用newValue
            print("部门编号新值:(newValue)")
            }
            didSet {    //定义no属性的didSet观察者,注意这里也没有声明参数,但是我们可以在观察者内部使用oldValue
            print("部门编号旧值:(oldValue)")  
            }
       }
       var name: String = "RESEARCH"
    }

    var emp = Employee()
    emp.no = 100
    emp.name = "Smith"    

    var dept = Department()
    dept.no = 30     
    上述代码运行结果如下:
    员工name新值:Smith
    员工name旧值:Tony
    部门编号新值:30
    部门编号旧值:10

    静态属性

    我先来设计一个类:有一个Account(银行账户)类,假设它有3个属性:amount(账户金额)、interestRate(利率)和owner(账户名)。

    在这3个属性中,amount和owner会因人而异,不同的账户这些内容是不同的,而所有账户的interestRate都是相同的。

    amount和owner属性与账户个体有关,称为实例属性。interestRate属性与个体无关,或者说是所有账户个体共享的,这种属性称为静态属性或类型属性。

    面向对象类型(结构体、枚举和类)都可以定义静态属性,它们的语法格式分别如下所示:

    struct 结构体名 {   //定义结构体, 结构体中可以定义静态存储属性和静态计算属性

    static var(或let) 存储属性  = "xxx"

    ...

    static var 计算属性名: 属性数据类型 {

    get {

    return 计算后属性值

    }

    set (新属性值) {

    ...

    }

    }

    }

    enum 枚举名 {  //定义枚举,枚举中不可以定义实例存储属性,但可以定义静态存储属性,也可以定义静态计算属性

    static var(或let) 存储属性  = "xxx"

    ...

    static var 计算属性名: 属性数据类型 {

    get {

    return 计算后属性值

    }

    set (新属性值) {

    ...

    }

    }

    }

    class 类名 {    //定义类,类中不仅可以定义实例存储属性,还可以定义静态存储属性

    static var(或let) 存储属性  = "xxx"

    ...

    class(或static)  var 计算属性名: 属性数据类型 {

    get {

    return 计算后属性值

    }

    set (新属性值) {

    ...

    }

    }

    }

    结构体静态计算属性也可以是只读的,语法如下:

    static var 计算属性名: 属性数据类型 {

    return 计算后属性值

    }

    看一个Account结构体静态属性示例:

    struct Account {//定义Account结构体

    var amount: Double = 0.0 //账户金额

    var owner: String = "" //账户名

    static var interestRate: Double = 0.0668  //定义静态存储属性interestRate利率

    static var staticProp: Double { //定义静态计算属性staticProp

    return interestRate * 1_000_000

    }

    var instanceProp: Double { //定义实例计算属性instanceProp

    return Account.interestRate * amount

    }

    }

    //访问静态属性

    print(Account.staticProp)

    var myAccount = Account()

    //访问实例属性

    myAccount.amount = 1_000_000

    //访问静态属性

    print(myAccount.instanceProp)

    ——Swift下标

    var studentList: String[]  = ["张三","李四","王五"]
    studentList[0] = "诸葛亮"

    var studentDictionary = [102: "张三",105: "李四", 109: "王五"]
    studentDictionary[110] = "赵六"

    在访问数组和字典的时候,可以采用下标访问。其中数组的下标是整数类型索引,字典的下标是它的“键”。

    Swift中的下标相当于Java中的索引属性和C#中的索引器。

    下标访问的语法格式如下:

    面向对象类型 类型名 {

    其他属性

    ...

    subscript(参数: 参数数据类型) -> 返回值数据类型 {

    get {

    return 返回值

    }

    set(新属性值) {

    ...

    }

    }

    }

    下标也有类似于计算属性的getter和setter访问器。

    getter访问器是一个方法,在最后使用return语句将计算结果返回。

    setter访问器“新属性值”是要赋值给属性值。参数的声明可以省略,系统会分配一个默认的参数newValue。

    在Swift中没有提供二维数组,只有一维数组Array。可以自定义一个二维数组类型,然后通过两个下标参数访问它的元素,形式上类似于C语言的二维数组。

    ——默认构造函数

    结构体和类的实例在构造过程中会调用一种特殊的init方法,称为构造函数。构造函数没有返回值,可以重载。在多个构造函数重载的情况下,运行环境可以根据它的外部参数名或参数列表调用合适的构造函数。

    结构体和类在构造过程中会调用一个构造函数,即便是没有编写任何构造函数,编译器也会提供一个默认的构造函数。下面看示例代码:

    class Rectangle {

    var Double  = 0.0

    var height: Double = 0.0

    }

    var rect = Rectangle() //创建实例,并调用默认构造函数init()
    rect.width = 320.0
    rect.height = 480.0
    print("长方形:(rect.width) x (rect.height)")

    Rectangle()表示调用了某个方法,这个方法就是默认构造函数init()。

    事实上,在Rectangle的定义过程中省略了构造函数,相当于如下代码:

    class Rectangle {

    var Double  = 0.0

    var height: Double = 0.0

    init() {

    }

    }

    如果Rectangle是结构体,则它的定义如下:

    struct Rectangle {

    var Double = 0.0

    var height: Double = 0.0

    }

    而结构体Rectangle的默认构造函数与类Rectangle的默认构造函数是不同的,相当于如下代码:

    struct Rectangle {

    var Double = 0.0

    var height: Double = 0.0

    init() {

    }

    init( Double, height: Double) { //有参数的构造函数

    self.width  = width

    self.height  = height

    }

    }

    要调用哪个构造函数是根据传递的参数名和参数类型决定的。

    ——构造函数与存储属性初始化

    构造函数的主要作用是初始化实例,其中包括:初始化存储属性和其它的初始化。在Rectangle类或结构体中,如果在构造函数中初始化存储属性width和height后,那么在定义他们时就不需要初始化了。

    Rectangle类代码如下:

    class Rectangle {

    var Double

    var height: Double

    init() {

    width  = 0.0

    height  = 0.0

    }

    }

    如果存储属性在构造函数中没有初始化,在定义的时候也没有初始化,那么就会发生编译错误。
    构造函数还可以初始化常量存储属性
    构造函数中的局部参数名可以直接作为外部参数名使用

    为了增强程序的可读性,Swift中的方法和函数可以使用外部参数名。在构造函数中也可以使用外部参数名。构造函数中的外部参数名要比一般的方法和函数更有意义,由于构造函数命名都是init,如果一个类型中有多个构造函数,我们可以通过不同的外部参数名区分调用不同的构造函数。

    ——构造函数重载

    构造函数作为一种特殊方法,也可以重载。
    Swift中构造函数可以多个,他们参数列表和返回值可以不同,这些构造函数构成重载。

    为了减少多个构造函数间的代码重复,在定义构造函数时,可以通过调用其他构造函数来完成实例的部分构造过程,这个过程称为构造函数代理。构造函数代理在结构体和类中使用方式是不同,先介绍结构体中构造函数代理。
    将上一节的示例修改如下:
    struct Rectangle {
        var Double
        var height: Double   
        init( Double, height: Double) {
            self.width   = width
            self.height  = height
        }   
        init(W Double,H height: Double) { 
            self.width   = width
            self.height  = height
        }   
        init(length: Double) {     //调用了self.init语句
            self.init(W: length, H: length)
        }
        init() {         //调用了self.init语句
            self.init( 640.0, height: 940.0)
        }
    }
    var rectc1 = Rectangle( 320.0, height: 480.0) 
    print("长方形:(rectc1.width) x (rectc1.height)")
    var rectc2 = Rectangle(W: 320.0, H: 480.0)   
    print("长方形:(rectc2.width) x (rectc2.height)")
    var rectc3 = Rectangle(length: 500.0)    
    print("长方形3:(rectc3.width) x (rectc3.height)")
    var rectc4 = Rectangle()       
    print("长方形4:(rectc4.width) x (rectc4.height)")
    将Rectangle声明为结构体类型,其中也有4个构造函数重载。
    这种在同一个类型中通过self.init语句进行调用当前类型其它构造函数,其它构造函数被称为构造函数代理。
    类构造函数横向代理
    由于类有继承关系,类构造函数代理比较复杂,分为横向代理和向上代理。
    · 横向代理类似于结构体类型构造函数代理,发生在同一类内部,这种构造函数称为便利构造函数(convenience initializers)。
    · 向上代理发生在继承情况下,在子类构造过程中要先调用父类构造函数,初始化父类的存储属性,这种构造函数称为指定构造函数(designated initializers)。

    构造函数调用规则

    在构造函数中可以使用构造函数代理帮助完成部分构造工作。类构造函数代理分为横向代理和向上代理,横向代理只能在发生在同一类内部,这种构造函数称为便利构造函数。向上代理发生在继承的情况下,在子类构造过程中,要先调用父类构造函数初始化父类的存储属性,这种构造函数称为指定构造函数。

    Person和Student类示例:
    class Person {
        var name: String
        var age: Int   
        func description() -> String {
            return "(name) 年龄是: (age)"
        }
        convenience init () {   //便利构造函数
            self.init(name: "Tony")
            self.age = 18
        }
        convenience init (name: String) { //便利构造函数
            self.init(name: name, age: 18)
        }
        init (name: String, age: Int) {  //指定构造函数
            self.name = name
            self.age  = age
        }
    }
    class Student: Person {
        var school: String
        init (name: String, age: Int, school: String) {  //指定构造函数
            self.school = school
            super.init(name: name, age: age)
        }
        convenience override init (name: String, age: Int) {//便利构造函数
            self.init(name: name, age: age, school: "清华大学")
        }
    }
    let student = Student()
    print("学生: (student.description())")
    构造函数之间的调用形成了构造函数链,如图所示。
    Swift限制构造函数之间的代理调用的规则有3条,如下所示。
    1、指定构造函数必须调用其直接父类的的指定构造函数。从图可见,Student中的④号指定构造函数调用Person中的③号指定构造函数。
    2、便利构造函数必须调用同一类中定义的其他构造函数。从图可见,Student中的⑤号便利构造函数调用同一类中的④号便利构造函数,Person中的①号便利构造函数调用同一类中的②号便利构造函数。
    3、便利构造函数必须最终以调用一个指定构造函数结束。从图可见,Student中的⑤号便利构造函数调用同一类中的④号指定构造函数,Person中的②号便利构造函数调用同一类中的③号指定构造函数。

    ——类的继承

    Swift中的继承只能发生在类上,不能发生在枚举和结构体上。一个类可以继承另一个类的方法、属性、下标等特征,当一个类继承其他类时,继承类叫子类,被继承类叫父类(或超类)。子类继承父类后,可以重写父类的方法、属性、下标等特征。
    为了了解继承性,看这样一个场景:一位面向对象的程序员小赵,在编程过程中需要描述和处理个人信息,于是他定义了类Person,如下所示:
    class Person {
        var name: String
        var age: Int   
        func description() -> String {
            return "(name) 年龄是: (age)"
        }
        init () {
            name = ""
            age  = 1
        }
    }
    一周以后,小赵又遇到了新的需求,需要描述和处理学生信息,于是他又定义了一个新的类Student,如下所示:
    class Student {
        var name: String
        var age: Int
       var school: String   
        func description() -> String {
            return "(name) 年龄是: (age)"
        }
        init() {
            school = ""
            name = ""
            age  = 8
        }
    }
    很多人会认为小赵的做法能够理解并相信这是可行的,但是问题在于Student和Person两个类的结构太接近了,后者只比前者多了一个属性school,却要重复定义其他所有的内容,实在让人“不甘心”。Swift提供了解决类似问题的机制,那就是类的继承,代码如下所示:
    class Student: Person {
        var school: String
        override init() {
            school = ""
            super.init()
            age  = 8
        }
    }
    Student类继承了Person类中的所有特征,“:”之后的Person类是父类。Swift中的类可以没有父类,例如Person类,定义的时候后面没有“:”,这种没有父类的就是基类。
    此外override init()是子类重写父类构造函数。
    一般情况下,一个子类只能继承一个父类,这称为单继承,但有的情况下一个子类可以有多个不同的父类,这称为多重继承。在Swift中,类的继承只能是单继承。多重继承可以通过遵从多个协议实现。也就是说,在Swift中,一个类只能继承一个父类,但是可以遵循多个协议。

    ——构造函数继承

    Swift中的子类构造函数的来源有两种:自己编写和从父类继承。并不是父类的所有的构造函数都能继承下来,能够从父类继承下来的构造函数是有条件的,如下所示。
    · 条件1:如果子类没有定义任何指定构造函数,它将自动继承所有父类的指定构造函数。
    · 条件2:如果子类提供了所有父类指定构造函数的实现,无论是通过条件1继承过来的,还是通过自己编写实现的,它都将自动继承所有父类的便利构造函数。(示例忽略)

    ——重写属性

    重写实例属性。我们可以在子类中重写从父类继承来的属性,属性有实例属性和静态属性之分,他们在具体实现也是不同的。
    实例属性的重写一方面可以重写getter和setter访问器,另一方面可以重写属性观察者。
    计算静态属性需要使用getter和setter访问器,而存储属性不需要。子类在继承父类后,也可以通过getter和setter访问器重写父类的存储属性和计算属性。
    从属性重写可见,子类本身并不存储数据,数据是存储在父类的存储属性中的。

    重写静态属性。

    在类中静态属性定义使用class或static关键字,但是使用哪一个要看子类中是否重写该属性。

    class修饰的属性可以被重写,static关键字就不能被重写。(示例忽略)

    这是我在学Swift整理的基础笔记,希望给更多刚学IOS开发者带来帮助,在这里博主非常感谢大家的支持!

    更多的请到参考我下一篇博文。之后还在持续更新中。。。

  • 相关阅读:
    ConcurrentHashMap使用示例
    vss的ss.ini丢失或损坏导致的vss无法登录错误
    Arcgis中用滚轮做放大缩小为什么和一般软件反向
    MapControl控件
    string截取字符串
    C# CheckedListBox控件用法总结(怎样得到多选的值)
    通信串口中报ObjectDisposedException错误时怎么解决
    C#串口SerialPort常用属性方法
    SerialPort.DataReceived 事件
    C#的串口编程
  • 原文地址:https://www.cnblogs.com/ZRJ-boke/p/6103975.html
Copyright © 2011-2022 走看看