前言
之前po过部分swift语法的笔记,以下再附上近期整理好的部分
类别(class)
class没有成员逐一建构器这个功能,而struct有。
class GameCharacter { var attackSpeed = 1.0 let gender : "" var name: String? // name为一个可选型别String?,会被自动指派为一个预设值nil,表示一开始没有name值。 // 这样可以不用在建构时给它值 init(gender : String) { //建构子 gender = "man" //建构时指派值 }}
继承:
class Boss : GameCharacter { // Boss继承自GameCharacter var name = String? = nil init(){ super.init(gender : "girl") }}
建构子
与函式跟方法一样,可以使用其内部参数名称作为外部参数名称,如下: // 定义一个结构 有两个建构器 struct Color { let red, green, blue: Double // 这个建构器有写外部参数名称跟内部参数名称 init(red r: Double, green g : Double, blue b: Double) { self.red = r self.green = g self.blue = b } // 这个建构器则是合併成一个参数名称 外部跟内部参数名称相同 init(white: Double) { red = white green = white blue = white } } var oneColor = Color(red: 0.9, green: 0.5, blue: 0.5) var anotherColor = Color(white: 1.0)
与函式跟方法一样,可以省略其外部参数名称,使用 下底线 _
替代外部参数名称,如下:
struct SomeNumbers { let number: Int init( _ n: Int) { // 使用下底线 _ 表示要省略外部参数名称 number = n } } // 生成一个实体时 参数前就不需要有外部参数名称 var oneNumbers = SomeNumbers(9)
结构(structure)
使用时机:
需要封装的资料量较少且较简单。
在指派或传递这个实体时,有特别需求这个资料是会被拷贝而不是参考。
不需要去继承另一个已存在型别的属性或行为。
所有struct都有一个自动生成的成员逐一建构器(memberwise initializer
),当要生成一个结构的实体时,用来初始化实体内的属性,如下:
struct CharacterStats { var hp = 0.0 var mp = 0.0 } var oneStats = CharacterStats(hp: 120, mp: 100) // 建构出实体 var anotherStats = oneStats // 这时修改 anotherStats 的 hp 属性 anotherStats.hp = 300 print(anotherStats.hp) // 可以看出来已经改变了 印出:300.0 // 但 oneStats 的属性不会改变 // 仍然是被生成实体时的初始值 印出:120.0 print(oneStats.hp)
属性(property)
被储存于常数的结构不能改变其内变数的值;而被储存于常数的类别可以改变其内变数的值
// 生成一个 CharacterStats 结构的实体 并指派给一个常数 someStatslet someStats = CharacterStats(hpValueMax: 900, mpValueMax: 150)someStats.hpValue = 1200 // 这行会报错误。这个实体 someStats 为一个常数 所以即使 hpValue 为一个变数属性 仍然不能修改hpValue的值
延迟储存属性
关键字 lazy
只能使用在变数,因为属性的值在实体建构完成之前可能无法得到,而常数属性在建构完成之前必须要有初始值。
使用延迟储存属性可以让类别中如果需要大量计算才能初始化的属性,在需要的时候才真的初始化它
class DataImporter { // 这个类别会导入外部档案并执行一些操作 初始化可能会花费不少时间 var fileName = "data.txt" // 这边简化成一个档案名称 实际上可能会有很多操作 } class DataManager { // 接着定义另一个类别 DataManager // 延迟储存属性 lazy var importer = DataImporter() // 操作时需要用到的资料 var data = [String]() // 简化内部内容 可能还有许多操作资料的动作 } let manager = DataManager() // 生成一个类别 DataManager 的实体常数 // 添加一些资料 manager.data.append("Some data") manager.data.append("Some more data") // 到目前为止 manager 的 importer 都尚未被初始化 // 直到第一次使用这个属性 才会被创建并初始化 print(manager.importer.fileName)
self 关键字类似 Java 的 this 关键字
覆写属性时,需要使用getter
(以及有时可省略的setter
)来覆写继承来的属性,且一定要写上属性的名称及型别,这样才能确定是从哪一个属性继承而来的。
可以将一个继承来的唯读属性覆写为一个读写属性,但不行将一个读写属性覆写为唯独属性。即原本有setter
的话,覆写时就一定要有setter
。
class AnotherHunter: Archer { // 覆写父类别的属性 重新实作 getter 跟 setter override var attackSpeed: Double { get { return 2.4 } set { print(newValue) } } // 省略其他内容 }
错误处理
Swift 提供了 do-catch 机制来拦截程式执行时发生的错误。
do{//会有例外丢出的函数呼叫放这let fm = FileManager.defaulttry fm.removeItem(atPath:"档名") //这边要加 try} catch {//抓到错误时的处理print(error.localizedDescription) //印出错误讯息}
如果想 自订错误的类型,必须用 enum
定义。以下为遵从 Error protocol 的型别 GoAfterGirlError
,用它来表达追求女生失败的各种原因:
enum GoAfterGirlError:Error { case poorProblem case tooYoungProblem case notAquariusProblem }
当程式判断错误发生时,必须用关键字 throw
丢出错误。而且唯有乖乖遵从 Error protocol 的型别,才能被当成错误丢出。
而当 function 里的程式码有可能丢出错误时,这个 function 的定义还必须加上 throws
,加在( ) 后,警告大家它很危险,有可能丢出错误 :
func goAfterAngelababy(money:Int, age:Int) throws { guard money > 10000 else { throw GoAfterGirlError.poorProblem } guard age > 18 else { throw GoAfterGirlError.tooYoungProblem } print("我追到 Angelababy 了!") } //当我们呼叫的 function 有可能丢出错误,也就是它有加上 throws 时,我们还要补上 try 才能呼叫。 try goAfterAngelababy(money: 1000, age: 30)
Swift 强制要求我们错误一定要处理,不处理将产生 compile error 提醒我们。
如果函式有回传值,而且可能返回 nil 的话,可以使用 try? 来呼叫函式,避免 APP 当掉
let ans = try? divided(10, by:0) //divided 函式如果分母为0时,会抛出错误,所以这边会回传nil