面向协议
- 面向对象的问题: 不能实现多继承,且会导致继承关系变得复杂
- 面向协议的事例:
protocol SportProtocol {
var hot: Double {get set} // {get set}表示可读可写
func playFootBall()
@objc optional func playBasketBall() // 前面的 @objc optional 表示方法是可选的
}
// 在协议中, 属性/方法可以在 extension 中默认实现
extension SportProtocol {
var price: Double { // 此时属性只读, 想要修改, 必须覆盖
return 100.0
}
func playFootBall() {
print("T足球")
}
}
import Foundation
import UIKit
protocol NibLoadProtocol {
}
// where Self: UIView 表示调动的必须是 UIView 及其子类
extension NibLoadProtocol where Self: UIView {
// 协议中 static 表示类方法
static func loadFromNib() -> Self {
return Bundle.main.loadNibNamed("\(self)", owner: nil, options: nil)?.first as! Self
}
}
枚举
- 枚举的语法,enum开头,每一行成员的定义使用case关键字开头,一行可以定义多个关键字
enum CompassPoint {
case North
case South
case East
case West
}
enum CompassPoint {
case North, South, East, West
}
上例中North,South,East,West的值并不等于0,1,2,3,而是他们本身就是自己的值,且该值的类型就是CompassPoint
var directionToHead = CompassPoint.West
// directionToHead是一个CompassPoint类型,可以被赋值为该类型的其他值
// 当设置directionToHead的值时,他的类型是已知的,因此可以省略East的类型
directionToHead = .East
- 使用switch分开枚举的值,以进行的不同的操作。switch内的case必须包含枚举的所有分支,否则编译出错。当然,列举所有枚举值不太方便时,可以使用default
directionToHead = .South
switch directionToHead {
case .North:
println("Lots of planets have a north")
case .South:
println("Watch out for penguins")
case .East:
println("Where the sun rises")
case .West:
println("Where the skies are blue")
}
- 枚举的元素可以是结合值(associated value),下面通过一个可以存储一维条形码(由3个整数组成)和二维条形码(由字符串组成)的枚举条形码实例来说明
enum Barcode {
case UPCA(Int, Int, Int)
case QRCode(String)
}
// 定义一个变量。该变量即可被赋值为3个整数,又可被赋值为一个字符串,但都是Barcode类型的枚举值
var productBarcode = Barcode.UPCA(8, 85909_51226, 3)
productBarcode = .QRCode("ABCDEFGHIJKLMNOP")
// 使用switch时,case内可区分条形码种类,可使用变量或常量获得结合值
switch productBarcode {
case .UPCA(let numberSystem, let identifier, let check):
println("UPC-A with value of \(numberSystem), \(identifier), \(check).")
case .QRCode(let productCode):
println("QR code with value of \(productCode).")
}
// 打印 "QR code with value of ABCDEFGHIJKLMNOP."
*在case内部,如果其类型都为let或var,则该关键字可提前到case和枚举类型中间,如:
case let .UPCA(numberSystem, identifier, check):
- 原始值类型的枚举在枚举名后紧跟数据类型,其枚举的成员在定义时已经赋予了初始值,且不能改变,与结合值类型的枚举相比,结合值是在将枚举值赋予一个变量时,才设置了那个枚举的值。
//原始值枚举的类型紧跟枚举名后,其成员的原始值的数据类型都是这个指定的类型
enum ASCIIControlCharacter: Character {
case Tab = "\t"
case LineFeed = "\n"
case CarriageReturn = "\r"
}
//Int类型的原始值枚举成员的原始值是递增的,比如Venus的值是2,Earth的值是3
enum Planet: Int {
case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}
//可以通过toRaw方法获得枚举成员的原始值
let earthsOrder = Planet.Earth.toRaw()
// earthsOrder 的值是 3,数据类型是Int
//可以通过fromRaw方法获得原始值对应的枚举成员
let possiblePlanet = Planet.fromRaw(7)
// possiblePlanet 的数据类型 Planet? 值是 Planet.Uranus
//因为fromRaw的原始值可能没有对应的枚举成员,所以返回的类型是一个可选变量值
let positionToFind = 9
if let somePlanet = Planet.fromRaw(positionToFind) {
switch somePlanet {
case .Earth:
println("Mostly harmless")
default:
println("Not a safe place for humans")
}
} else {
println("There isn't a planet at position \(positionToFind)")
}
// 枚举定义中没有原始值为9的成员,所以打印 "There isn't a planet at position 9"
枚举函数
//枚举函数
enum mobileLanguageFun{
case IOS (String, String)
case Android (String)
//定义枚举函数
var description: String{
switch self {
case mobileLanguageFun.IOS(let language1, let language2):
return "language1 = \(language1), language2 = \(language2)"
case mobileLanguageFun.Android(let temp):
return temp
default:
return ("NO")
}
}
}
var myMobile: mobileLanguageFun = mobileLanguageFun.IOS("objc", "swift")
println(myMobile.description) //language1 = objc, language2 = swift
错误处理与异常抛出
主动退出程序的几种情况
- Fatal Errors(致命的错误)
- 使用fatalError()函数可以立即终止你的应用程序,在fatalError()中可以给出终止信息。使用fatalError()函数,会毫无条件的终止你的应用程序,用起来也是比较简单的,就是一个函数的调用。
fatalError("致命错误,调用我程序终止")
- Assertions(断言)
- 在断言中的提示条件是可选的。断言会在Debug模式下起作用,但是在Release版本中就会被忽略。
// @param: condition, 为true时,断言不执行,相应的断言信息不打印。为false时,断言执行,并且打印相应的断言信息。
assert(<#T##condition: Bool##Bool#>, <#T##message: String##String#>)
- 先决条件(Preconditions)
- Preconditions的用法和断言一样,不过有一点需要主要,Preconditions在debug和release模式下都会被执行,除非使用–Ounchecked进行编译。
precondition(<#T##condition: Bool##Bool#>, <#T##message: String##String#>)
Swift中的错误处理
- 在Swift中,如果你要定义你自己的错误类型,你只需要实现ErrorType协议即可。声明完错误类型后,就可以在处理错误抛出异常时使用自定义的错误类型了。
1.使用枚举创建错误类型
- 1.1遵循ErrorType协议,自定义错误类型。下方定义了一个错误类型枚举,该枚举遵循了ErrorType协议,在接下来的代码中我们将会使用这个MyCustomErrorType枚举
// 定义错误类型
enum MyErrorType: ErrorType {
case ErrorReason1
case ErrorReason2
case ErrorReason3
}
- 1.2在我们的函数定义时可以使用throws关键字,以及在函数中使用
throw
关键字对错误进行抛出,抛出的错误类型就可以使用上面我们自己定义的错误类型。
func throwError() throws {
let test: Int? = nil
guard test != nil else {
throw MyErrorType.ErrorReason2
}
}
- 1.3上面函数的功能是对错误进行抛出,接下来就该使用
do-catch
来处理抛出的错误。使用try
对错误进行捕捉,使用do-catch
对错误进行处理。
do {
try throwError()
} catch MyErrorType.ErrorReason1 {
print("错误原因: 1")
}catch MyErrorType.ErrorReason2 {
print("错误原因: 2")
}catch {
print("错误原因: 3")
}
2.使用结构体为错误处理添加Reason
- 使用结构体来遵循ErrorType协议, 可以在抛出错误时,给自定义错误类型提供错误原因。
- 2.1使用结构体创建错误类型,下方名为MyErrorType的结构体遵循了ErrorType协议,并且在MyErrorType结构体中,声明了一个reason常量,该reason常量中存储的就是错误原因
struct MyErrorType2: ErrorType {
let reason1: String
let reason2: String
}
- 2.2上面定义完错误类型结构体后,在错误抛出中就可以使用了。在错误抛出时,可以传入一个错误原因
func throwError2() throws {
let test: Int? = nil
guard test != nil else {
throw MyErrorType2(reason1: "原因1", reason2: "原因2")
}
}
- 2.3最后要对抛出的错误进行do-catch处理,在处理时,可以对错误原因进行打印,错误原因存储在error中
do {
try throwError2()
} catch {
print(error)
}
3.使String类型遵循ErrorType协议,直接使用String提供错误原因
extension String: ErrorType {
}
func throwError3() throws {
let test: Int? = nil
guard test != nil else {
throw "error"
}
}
do {
try throwError3()
} catch {
print(error)
}
网友评论