Swift基础语法-4

204 阅读10分钟

十、方法

1、实例方法

实例方法(Instance Method):通过实例对象调用

2、类型方法

类型方法( Type Method ) : 通过类型调用,用static或者class关键字定义

3、self

  • 在类型方法中代表类型

  • 在实例方法中代表实例对象

  • 在类型方法static func getCount中

    • count等于self.count、Car.self.count、Car.count
     class Car {
          static var count = 0
          init() {
              Car.count += 1
          }
          static func getCount() ->Int{count}
      }
      
      let c0 = Car()
      let c1 = Car()
      let c2 = Car()
      print(Car.getCount())//3

4、在实例方法中修改值类型

要使用 可变方法,将关键字 mutating 放到方法的 func 关键字之前就可以了

struct Point {
    var x = 0.0, y = 0.0
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        x += deltaX
        y += deltaY
    }
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
print("The point is now at (\(somePoint.x), \(somePoint.y))")
// 打印“The point is now at (3.0, 4.0)”

5、@discardableResult

在func前面加个@discardableResult,可以消除:函数调用后返回值未被使用的警告

十一、下标

1、下标语法

下标允许你通过在实例名称后面的方括号中传入一个或者多个索引值来对实例进行查询

subscript(index: Int) -> Int {
    get {
      // 返回一个适当的 Int 类型的值
    }
    set(newValue) {
      // 执行适当的赋值操作
    }
}

2、下标用法

  • subscript中定义的返回值类型决定了
  • get方法的返回值类型
  • set方法中newVa lue的类型
  • subscript可以接受多个参数,并且类型任意
    class Point {
        var x = 0.0,y = 0.0
        subscript( index: Int) -> Double {
            set {
                if index==0{
                    x = newValue
                } else if
                    index == 1 {
                    y = newValue
                }
            }
            get {
                if index==0{
                    return x
                }else if index==1 {
                    return y
                }
                return 0
            }
        }
    }

3、下标细节

  • subscript可以没有set方法,但必须要有get方法
  • 如果只有get方法,可以省略get
  • 可以设置参数标签
  • 下标可以是类型方法

十二、继承

1、定义

一个类可以继承另一个类的方法,属性和其它特性。当一个类继承其它类时,继承类叫子类,被继承类叫超类(或父类)

  • 值类型(枚举、结构体)不枝持继承,只有类支持继承
  • 没有父类的类,称为:基类
  • Swift并没有像OC、Java那样的规定:任何类最终都要继承自某个基类
  • 子类可以重写父类的下标、方法、属性,重写必须加上override关键字

2、重写类型方法、下标

  • 被class修饰的类型方法、下标,允许被子类重写
  • 被static修饰的类型方法、下标,不允许被子类重写

3、重写属性

  • 子类可以将父类的属性(存储、计算)重写为计算属性
  • 类不可以将父类属性重写为存储属性
  • 只能重写var属性,不能重写let属性
  • 重写时,属性名、类型要一致
  • 子类重写后的属性权限不能小于父类属性的权限
    • 如果父类属性是只读的,那么子类重写后的属性可以是只读的、也可以是可读写的
    • 如果父类属性是可读写的,那么子类重写后的属性也必须是可读写的

4、重写类型属性

  • 被class修饰的计算类型属性,可以被子类重写
  • 被static修饰的类型属性(存储、计算) ,不可以被子类重写

5、属性观察器

  • 可以在子类中为父类属性(除了只读计算属性、let属性 )增加属性观察器

6、final

  • 被final修饰的方法、下标、属性,禁止被重写
  • 被final修饰的类,禁止被继承

十三、构造过程

1、初始化器

  • 类结构体、枚举都可以定义初始化器
  • 类有2种初始化器:指定初始化器( designated initializer)、便捷初始化器( convenience initializer )
    //指定初始化器
      init(parameters) {
      		 statements
         }
      //便捷初始化器
      convenience init(parameters) {
           statements
         }
  • 每个类至少有一个指定初始化器,指定初始化器是类的主要初始化器
  • 默认初始化器总是类的指定初始化器
  • 类偏向于少量指定初始化器, 一个类通常只有一个指定初始化器 初始化器的相互调用规则
    • 指定初始化器必须从它的直系父类调用指定初始化器
    • 便捷初始化器必须从相同的类里调用一个初始化器
    • 便捷初始化器最终必须调用一个指定初始化器

2、重写

  • 当重写父类的指定初始化器时,必须加上override (即使子类的实现是便捷初始化器)
  • 如果子类写了一个匹配父类便捷初始化器的初始化器,不用加上override
  • 因为父类的便捷初始化器永远不会通过子类直接调用,因此,严格来说,子类无法重写父类的便捷初始化器

3、自动继承

①如果子类没有自定义任何指定初始化器,它会自动继承父类所有的指定初始化器
②如果子类提供了父类所有指定初始化器的实现(要么通过方式①继承,要么重写)

  • 子类自动继承所有的父类便捷初始化器 ③就算子类添加了更多的便捷初始化器,这些规则仍然适用
    ④子类以便捷初始化器的形式重写父类的指定初始化器,也可以作为满足规则②的一部分

4、require

  • 用required修饰指定初始化器,表明其所有子类都必须实现该初始化器(通过继承或者重写实现)

  • 如果子类重写了required初始化器,也必须加上required ,不用加override

    class Person{
        required init(){
            init(age: Int){}
        }
    }
    class Student: Person {
        required init() {
            super.init()
        }
    }
    

5、属性观察器

  • 父类的属性在它自己的初始化器中赋值不会触发属性观察器,但在子类的初始化器中赋值会触发属性观察器

       class Person {
        var age:Int {
            willSet{
                print("willSet",newValue)
            }
            didSet {
                print("didSet",oldValue,age)
            }
        }
        init() {
            self.age = 0
        }
    }
    class Student: Person {
        override init() {
            super.init()
            self.age = 1
        }
    }
    
    var stu = Student()//willSet 1
    									 //didSet 0 1
    

6、可失败初始化器

  • 类、结构体、枚举都可以使用init?定义可失败初始化器

  • 不允许同时定义参数标签、参数个数、参数类型相同的可失败初始化器和非可失败初始化器

  • 可以用init !定义隐式解包的可失败初始化器

  • 可失败初始化器可以调用非可失败初始化器,非可失败初始化器调用可失败初始化器需要进行解包

  • 如果初始化器调用一个可失败初始化器导致初始化失败,那么整个初始化过程都失败,并且之后的代码都停止执行

  • 可以用一个非可失败初始化器重写一个可失败初始化器,但反过来是不行的

    class Person {
        var name: String
        init?(name: String) {
            if name.isEmpty {
                return nil
            }
            self.name = name
        }
        
    }
    var test1 = Person.init(name: "ss")
    if let a = Person.init(name: "acs"){
        print(a.name)
    }
    print(test1!.name)//acs ss
    

十四、析构过程

  • deinit叫做反初始化器,类似于C+ +的析构函数、0C中的dealloc方法
    • 当类的实例对象被释放内存时,就会调用实例对象的deinit方法 class Person { deinit{ print("Person对象销毁了") } }
      • deinit不接受任何参数,不能写小括号,不能自行调用
      • 父类的deinit能被子类继承
      • 子类的deinit实现执行完毕后会调用父类的deinit

十五、可选链

  • 如果可选项为nil ,调用方法、下标、 属性失败,结果为nil
  • 如果可选项不为nil ,调用方法、下标、属性成功,结果会被包装成可选项
  • 如果结果本来就是可选项,不会进行再次包装
  • 多个?可以链接在一起
    • 如果链中任何一个节点是nil ,那么整个链就会调用失败
      var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
      testScores["Dave"]?[0] = 91
      testScores["Bev"]?[0] += 1
      testScores["Brian"]?[0] = 72
      // "Dave" 数组现在是 [91, 82, 84],"Bev" 数组现在是 [80, 94, 81]
  • 可选项的本质是enum类型

十六、错误处理

1、定义

错误处理(Error handling)是响应错误以及从错误中恢复的过程

2、表示与抛出错误

  • 在 Swift 中,错误用遵循 Error 协议的类型的值来表示。这个空协议表明该类型可以用于错误处理。

    enum VendingMachineError: Error {
        case invalidSelection                     //选择无效
        case insufficientFunds(coinsNeeded: Int) //金额不足
        case outOfStock                             //缺货
    }
    throw VendingMachineError.insufficientFunds(coinsNeeded: 5)
    

3、处理错误

  • 把函数抛出的错误传递给调用此函数的代码
  • 用 do-catch 语句处理错误
  • 将错误作为可选类型处理
  • 断言此错误根本不会发生

4、用 throwing 函数传递错误

为了表示一个函数、方法或构造器可以抛出错误,在函数声明的参数之后加上 throws 关键字。一个标有 throws 关键字的函数被称作 throwing 函数。如果这个函数指明了返回值类型,throws 关键词需要写在返回箭头(->)的前面。

func canThrowErrors() throws -> String   
func cannotThrowErrors() -> String
struct Item {
    var price: Int
    var count: Int
}
enum VendingMachineError: Error {
    case invalidSelection                     //选择无效
    case insufficientFunds(coinsNeeded: Int) //金额不足
    case outOfStock                             //缺货
}
class VendingMachine {
    var inventory = [
        "Candy Bar": Item(price: 12, count: 7),
        "Chips": Item(price: 10, count: 4),
        "Pretzels": Item(price: 7, count: 11)
    ]
    var coinsDeposited = 0

    func vend(itemNamed name: String) throws {
        guard let item = inventory[name] else {
            throw VendingMachineError.invalidSelection
        }

        guard item.count > 0 else {
            throw VendingMachineError.outOfStock
        }

        guard item.price <= coinsDeposited else {
            throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)
        }

        coinsDeposited -= item.price

        var newItem = item
        newItem.count -= 1
        inventory[name] = newItem

        print("Dispensing \(name)")
    }
}

5、rethrows

  • rethrows表明:函数本身不会抛出错误,但调用闭包参数抛出错误,那么它会将错误向上拋

6、defer

  • defer语句:用来定义以任何方式(抛错误、return等 )离开代码块前必须要执行的代码
  • defer语句将延迟至当前作用域结束之前执行

十七、类型转换

1、类型转换

类型转换可以判断实例的类型,也可以将实例看做是其父类或者子类的实例。

类型转换在 Swift 中使用 is 和 as 操作符实现。

  • is用来判断是否为某种类型
  • as用来做强制类型转换

2、检查类型

用类型检查操作符(is)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 true,否则返回 false。

3、向下转型

某类型的一个常量或变量可能在幕后实际上属于一个子类。当确定是这种情况时,你可以尝试用类型转换操作符(as? 或 as!)向下转到它的子类型

  • 转换没有真的改变实例或它的值。根本的实例保持不变;只是简单地把它作为它被转换成的类型来使用。

4、Any 和AnyObject 的类型转换

  • Swift提供了2种特殊的类型: Any、Any0bject
    • Any :可以代表任意类型(枚举、结构体、类,也包括函数类型)
    • Any0bject :可以代表任意类类型(在协议后面写上: Any0bject代表只有类能遵守这个协议)
    • 在协议后面写上: class也代表只有类能遵守这个协议

十八、嵌套类型

枚举常被用于为特定类或结构体实现某些功能。类似地,枚举可以方便的定义工具类或结构体,从而为某个复杂的类型所使用。为了实现这种功能,Swift 允许你定义嵌套类型,可以在支持的类型中定义嵌套的枚举、类和结构体