Swift 进阶 | 青训营笔记

65 阅读5分钟

这是我参与「第四届青训营 」笔记创作活动的第13天

类是一种通用而又灵活的结构,可以定义常量、变量和函数的语法,定义属性、添加方法

类的定义

一个类可以继承另一个类的方法,属性和其它特性。当一个类继承其它类时,继承类叫子类,被继承类叫超类(或父类)。不继承于其它类的类,称之为基类。类的继承语法结构如下

 class SomeClass {
     var name: String
     // code
     init(name: String) {
        self.name = name
    }
 }
 // 继承 ":"后面跟超类
class SomeClassInherited: SomeClass {
     // code
}

类的实例化

类能使用初始化器语法来生成新的实例。初始化器语法最简单的是在类或结构体名字后面接一个空的圆括号。新实例属性的初始化值可以通过属性名称传递到成员初始化器中。若类中含有未被初始化的变量属性,在初始化类时,会要求实现初始化语法,对变量进行赋值

class SomeClass {
     var name: String
     // 要求实现初始化语法
     init(name: String) {
        self.name = name
    }
}

// init
let someClass = SomeClass(name: "Anna")

class SomeClass2 {
    // 变量已经赋值,无需初始化语法
     var name: String = ""
}

// init
let someClass2 = SomeClass2()

类的访问

点语法来访问一个实例的属性。在点语法中,你只需在实例名后面书写属性名,用( .)来分开,无需空格, 也可以用点语法赋值一个值到类的变量属性中

print(someClass.name)    // Anna
someClass.name = "Tom"

类的扩展

和其他语言一样,类也可以用 extension 对类进行扩展。扩展为现有的类、结构体、枚举类型、或协议添加了新功能。这也包括了为无访问权限的源代码扩展类型的能力(即所谓的逆向建模

extension SomeClass {
    // new functionality to add to SomeType goes here }
  • 添加计算实例属性和计算类型属性

    extension Double {
    var km: Double { return self * 1_000.0 }
    var m: Double { return self }
    var cm: Double { return self / 100.0 }
    var mm: Double { return self / 1_000.0 }
    var ft: Double { return self / 3.28084 }
    }
    
  • 定义实例方法和类型方法

    extension Int {
        func repetitions(task: () -> Void) {
            for _ in 0..<self {
                task()
            }
        }
    }
    
  • 提供新初始化器

    extension Rect {
        init(center: Point, size: Size) {
            let originX = center.x - (size.width / 2)
            let originY = center.y - (size.height / 2)
            self.init(origin: Point(x: originX, y: originY), size: size)
        }
    }
    

类是引用类型

引用类型被赋值到一个常量,变量或者本身被传递到一个函数的时候它是不会被拷贝的。相对于拷贝,这里使用的是同一个对现存实例的引用。形如以下代码,a与b指向的是同一块内存地址

var someClass = SomeClass(name: "Anna")
var someClassCopy = someClass
someClassCopy.name = "Tom"
print(someClass.name)
// "Tom"

协议

基本定义

协议为方法、属性、以及其他特定的任务需求或功能定义蓝图。协议可被类、结构体、或枚举类型采纳以提供所需功能的具体实现。满足了协议中需求的任意类型都叫做遵循了该协议。

除了指定遵循类型必须实现的要求外,你可以扩展一个协议以实现其中的一些需求或实现一个符合类型的可以利用的附加功能

protocol SomeProtocal {
    //protocal definition goes here
}

协议可以要求所有遵循该协议的类型提供特定名字和类型的实例属性或类型属性。协议并不会具体说明属性是储存型属性还是计算型属性——它只具体要求属性有特定的名称和类型。协议同时要求一个属性必须明确是可读的或可读的和可写的。

若协议要求一个属性为可读和可写的,那么该属性要求不能用常量存储属性或只读计算属性来满足。若协议只要求属性为可读的,那么任何种类的属性都能满足这个要求,而且如果你的代码需要的话,该属性也可以是可写的。

属性要求定义为变量属性,在名称前面使用 var 关键字。可读写的属性使用 { get set } 来写在声明后面来明确,使用 { get } 来明确可读的属性。

protocol SomeProtocol {
    var mustBeSettable: Int { get set }
    var doesNotNeedToBeSettable: Int { get }
}

协议的使用

协议可以在很多其他类型可以使用的地方使用,包括:

  • 在函数、方法或者初始化器里作为形式参数类型或者返回类型
  • 作为常量、变量或者属性的类型
  • 作为数组、字典或者其他存储器的元素的类型
protocol SomeProtocol { 
    //protocol definition goes here 
}

func someFunction (protocol: SomeProtocol) {}

var someProtocol: SomeProtocol

var someProtocols: [SomeProtocol]

扩展extension

如果一个类型已经遵循了协议的所有需求,但是还没有声明它采纳了这个协议,你可以让通过一个空的扩展来让它采纳这个协议。例如 LinearCongruentialGenerator,除了上面那种形式,还可以直接用 extension 表示它已经遵循了该协议

extension RandomSelectStudent: RandomNumberGenerator { } 

协议的其他变型

  • mutating:表示该方法可以改变所属的实例
  • required:实现指定初始化器或便捷初始化器来使遵循该协议的类满足协议的初始化器要求。在这两种情况下,你都必须使用 required 关键字修饰初始化器的实现
  • required & override:如果一个子类重写了父类指定的初始化器,并且遵循协议实现了初始化器要求,那么就要为这个初始化器的实现添加 required 和 override 两个修饰符,但如果子类带final那就不用再用required修饰来
  • 协议的多个使用:直接用逗号分隔或 & 这里 SomeSuperClassSomeProtocal 是两个协议,Named & Aged 也是两个协议
class SomeSubClass: SomeSuperClass, SomeProtocol {
    // "required" from SomeProtocol conformance; "override" from SomeSuperClass    required override init() {
        // initializer implementation goes here    }
}

func wishHappyBirthday(to celebrator: Named & Aged) {
    print("Happy birthday, (celebrator.name), you're (celebrator.age)!")
}