了解模式之前,我们先看下什么是字面量。
var age = 10
var isShow = false
var name = "Jack"
上面代码中的10
,false
,"Jack"
就是字面量。
常见字面量的默认类型:
public typealias IntegerLiteralType = Int
public typealias FloatLiteralType = Double
public typealias BooleanLiteralType = Bool
public typealias StringLiteralType = String
可以通过typealias
修改字面量的默认类型(一般也没必要修改):
public typealias FloatLiteralType = Float
public typealias IntegerLiteralType = UInt8
var age = 10 // UInt8类型
var height = 20.0 // Float类型
Swift自带的绝大部分类型,都支持直接通过字面量进行初始化(不需要直接调用初始化器):
Bool、Int、Float、Double、String、Array、Dictionary、Set、Optional等
Swift自带类型之所以能够通过字面量初始化,是因为它们遵守了对应的协议。
Bool : ExpressibleByBooleanLiteral
Int : ExpressibleByIntegerLiteral
Float、Double : ExpressibleByIntegerLiteral、ExpressibleByFloatLiteral
Dictionary : ExpressibleByDictionaryLiteral
String : ExpressibleByStringLiteral
Array、Set : ExpressibleByArrayLiteral
Optinal : ExpressibleByNilLiteral
示例代码:
var b: Bool = false // ExpressibleByBooleanLiteral
var i: Int = 10 // ExpressibleByIntegerLiteral
var f0: Float = 10 // ExpressibleByIntegerLiteral
var f1: Float = 10.0 // ExpressibleByFloatLiteral
var d0: Double = 10 // ExpressibleByIntegerLiteral
var d1: Double = 10.0 // ExpressibleByFloatLiteral
var s: String = "idbeny" // ExpressibleByStringLiteral
var arr: Array = [1, 2, 3] // ExpressibleByArrayLiteral
var set: Set = [1, 2, 3] // ExpressibleByArrayLiteral
var dict: Dictionary = ["name" : "daben"] // ExpressibleByDictionaryLiteral
var o: Optional<Int> = nil // ExpressibleByNilLiteral
示例代码一:
Bool
类型直接赋值给Int
类型的变量,会直接报错:
如果要不报错,只需要给Int
添加一个遵守协议的扩展即可:
extension Int : ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = value ? 1 : 0
}
}
var num: Int = true
print(num) // 输出:1
示例代码二:
class Student: ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral, ExpressibleByStringLiteral, CustomStringConvertible {
var name: String = ""
var score: Double = 0
required init(floatLiteral value: Double) {
self.score = value
}
required init(integerLiteral value: Int) {
self.score = Double(value)
}
required init(stringLiteral value: String) {
self.name = value
}
// 支持Unicode和特殊字符
required init(unicodeScalarLiteral value: String) {
self.name = value
}
required init(extendedGraphemeClusterLiteral value: String) {
self.name = value
}
var description: String {
"name=\(name), score=\(score)"
}
}
var stu: Student = 90
print(stu) // 输出:name=, score=90.0
stu = 98.5
print(stu) // 输出:name=, score=98.5
stu = "idbeny"
print(stu) // 输出:name=idbeny, score=0.0
示例代码三:
struct Point {
var x = 0.0, y = 0.0
}
extension Point : ExpressibleByArrayLiteral, ExpressibleByDictionaryLiteral {
init(arrayLiteral elements: Double...) {
guard elements.count > 0 else {
return
}
self.x = elements[0]
guard elements.count > 1 else {
return
}
self.y = elements[1]
}
init(dictionaryLiteral elements: (String, Double)...) {
for (k, v) in elements {
if k == "x" {
self.x = v
} else if k == "y" {
self.y = v
}
}
}
}
var p: Point = [5.12, 10.24]
print(p) // 输出:Point(x: 5.12, y: 10.24)
p = ["x" : 11, "y" : 22]
print(p) // 输出:Point(x: 11.0, y: 22.0)
模式是用于匹配的规则,比如switch
的case
、捕捉错误的catch
、if/guard/while/for
语句的条件等。
Swift中的模式有:
_
匹配任何值_?
匹配非nil
值示例代码:
enum Life {
case human(name: String, age: Int?)
case animal(name: String, age: Int?)
}
func check(_ life: Life) {
switch life {
case .human(let name, _):
print("human", name)
case .animal(let name, _?):
print("animal", name)
default:
print("other")
}
}
check(.human(name: "Rose", age: 20)) // 输出:human Rose
check(.human(name: "Jack", age: nil)) // 输出:human Jack
check(.animal(name: "Dog", age: 5)) // 输出:animal Dog
check(.animal(name: "Cat", age: nil)) // 输出:other
case .human(let name, _):
中的_
就是匹配任意值。
case .animal(let name, _?):
中的_?
就是匹配非nil
值。
给对应的变量、常量名赋值。
var age = 10
let name = "idbeny"
把对应位置的值绑定到变量/常量上。
let point = (3, 2)
switch point {
case let (x, y):
print("x:\(x), y:\(y)")
}
// 输出:x:3, y:2
本质也是值绑定和通配符模式。
示例代码一(数组):
let points = [(0, 0), (1, 0), (2, 0)]
for (x, _) in points {
print(x)
}
/*
输出 :
0
1
2
*/
示例代码二(case):
let name: String? = "idbeny"
let age = 18
let info: Any = [1, 2]
switch (name, age, info) {
case (_?, _, _ as String):
print("case")
default:
print("default")
}
// 输出:default
示例代码三(字典):
var scores = ["jack" : 98, "rose" : 100, "kate" : 86]
for (name, score) in scores {
print(name, score)
}
/*
输出:
jack 98
kate 86
rose 100
*/
if case
语句等价于只有1个case
的switch
语句。
示例代码一:
let age = 2
func test() {
// 原来的写法
if age >= 0 && age <= 9 {
print("[0, 9]")
}
// 枚举case模式
if case 0...9 = age {
print("[0, 9]")
}
guard case 0...9 = age else {
return }
print("[0, 9]")
}
test()
/*
输出:
[0, 9]
[0, 9]
[0, 9]
*/
if case 0...9 = age
可以理解为是拿出age
的值和case
后面的条件进行匹配。
下面的代码完全等价上面示例代码的if case
:
switch age {
case 0...9:
print("[0, 9]")
default:
break
}
// 输出:[0, 9]
示例代码二:
let age = 2
let ages: [Int?] = [2, 3, nil, 5]
for case nil in ages {
print("有nil值")
break
}
// 输出:有nil值
示例代码三:
let points = [(1, 0), (2, 1), (3, 0)]
for case let (x, 0) in points {
print(x)
}
// 输出:1 3
// 错误写法:
// for (x, 0) in points {
// print(x)
// }
示例代码一:
let age: Int? = 42
if case .some(let x) = age {
print(x)
}
// 输出:42
// x?代表非空
if case let x? = age {
print(x)
}
// 输出:42
示例代码二:
let ages: [Int?] = [nil, 2, 3, nil, 5]
for case let age? in ages {
print(age)
}
/*
输出:
2
3
5
*/
上面示例二的代码和下面的代码等效:
for item in ages {
// 可选项绑定
if let age = item {
print(age)
}
}
/*
输出:
2
3
5
*/
示例代码三:
func check(_ num: Int?) {
switch num {
case 2?:
print("2")
case 4?:
print("4")
case 6?:
print("6")
case _?:
print("other")
case _:
print("nil")
}
}
check(4) // 输出:4
check(8) // 输出:other
check(nil) // 输出:nil
主要是is
和as
的用法。
示例代码一:
let num: Any = 6
switch num {
case is Int:
print("is Int", num)
default:
break
}
// 输出:is Int 6
case is Int
仅仅是判断num
是否为Int
类型,编译器不会自动强转,依然认为num
是Any
类型。
如果需要强转并且判断类型,可以使用as
:
switch num {
case let n as Int:
print("as Int", n)
default:
break
}
// 输出:as Int 6
此时的n
是Int
类型,但是num
依然是Any
类型。如果num
不是Int
类型就会跳过当前case
,匹配下一个case
。
示例代码二:
class Animal {
func eat() {
print(type(of: self), "eat")
}
}
class Dog : Animal {
func run() {
print(type(of: self), "run")
}
}
class Cat : Animal {
func jump() {
print(type(of: self), "jump")
}
}
func check(_ animal: Animal) {
switch animal {
case let dog as Dog:
dog.eat()
dog.run()
case is Cat:
animal.eat()
default:
break
}
}
check(Dog())
/*
输出:
Dog eat
Dog run
*/
check(Cat())
/*
输出:
Cat eat
*/
上面的示例中,如果匹配case is Cat
时,怎样才能执行jump
方法?因为animal
是Animal
类型。
可以对animal
进行强制转换:
(animal as? Cat)?.jump()
表达式模式用在case
中:
let point = (1, 2)
switch point {
case (0, 0):
print("(0, 0) is at the origin.")
case (-2...2, -2...2):
print("(\(point.0), \(point.1)) is near the origin.")
default:
print("The point is at (\(point.0), \(point.1)).")
}
// 输出:(1, 2) is near the origin.
通过汇编查看上面示例代码,发现示例程序是用~=
运算符做匹配:
其实,在Swift中,一些复杂
switch
匹配会用到~=
运算符,但并不是所有的switch
都是用到该运算符。
可以通过重载运算符,自定义表达式模式的匹配规则。
示例代码一:
struct Student {
var score = 0, name = ""
static func ~= (pattern: Int, value: Student) -> Bool {
value.score >= pattern
}
static func ~= (pattern: ClosedRange<Int>, value: Student) -> Bool {
pattern.contains(value.score)
}
static func ~= (pattern: Range<Int>, value: Student) -> Bool {
pattern.contains(value.score)
}
}
使用示例代码一:
var stu = Student(score: 72, name: "idbeny")
switch stu {
case 100: print(">=100")
case 90: print(">=90")
case 80..<90: print("[80, 90)")
case 60...79: print("[60, 79]")
case 0: print(">=0")
default: break
}
// 输出:[60, 79]
stu
是怎么和Int
、Rang
进行匹配的呢?重写~=
运算符。
基本上是固定写法(返回值必须是Bool
):
// pattern: case后面的类型
// value: switch后面的类型
static func ~= (pattern: Int, value: Student) -> Bool {
}
示例代码二(if case
本质就是switch
):
if case 60 = stu {
print(">=60")
}
// 输出:>=60
示例代码三:
var info = (Student(score: 70, name: "daben"), "及格")
switch info {
case let (60, text):
print(text)
default:
break
}
// 输出:及格
示例代码四:
extension String {
static func ~= (pattern: (String) -> Bool, value: String) -> Bool {
pattern(value)
}
}
func hasPrefix(_ prefix: String) -> ((String) -> Bool) {
{
$0.hasPrefix(prefix) }
}
func hasSuffix(_ suffix: String) -> ((String) -> Bool) {
{
$0.hasSuffix(suffix) }
}
var str = "idbeny"
switch str {
case hasPrefix("i"), hasSuffix("y"):
print("以i开头 或 以y结尾")
default:
break
}
// 输出:以i开头 或 以y结尾
示例代码五:
func isEven(_ i: Int) -> Bool {
i % 2 == 0
}
func isOdd(_ i: Int) -> Bool {
i % 2 != 0
}
extension Int {
static func ~= (pattern: (Int) -> Bool, value: Int) -> Bool {
pattern(value)
}
}
var age = 10
switch age {
case isEven:
print("偶数")
case isOdd:
print("奇数")
default:
print("其他")
}
// 输出:偶数
还可以定义更多自定义操作符:
prefix operator ~>
prefix operator ~>=
prefix operator ~<
prefix operator ~<=
prefix func ~> (_ i: Int) -> ((Int) -> Bool) {
{
$0 > i } }
prefix func ~>= (_ i: Int) -> ((Int) -> Bool) {
{
$0 >= i } }
prefix func ~< (_ i: Int) -> ((Int) -> Bool) {
{
$0 < i } }
prefix func ~<= (_ i: Int) -> ((Int) -> Bool) {
{
$0 <= i } }
switch age {
case ~>=0, ~<=10:
print("1")
case ~>10, ~<20:
print("2")
default:
break
}
// 输出:1
>, =, >=, <, <=
运算符都是中缀运算符,为了不影响原有的运算符特性,在原有运算符前面加一个~
符号成为一个新的运算符。
可以使用where
为模式匹配增加匹配条件。
示例代码一(case
):
var data = (10, "Jack")
switch data {
case let (age, _) where age > 10:
print(data.1, "age>10")
case let (age, _) where age > 0:
print(data.1, "age>0")
default:
break
}
// 输出:Jack age>0
示例代码二(for
):
var ages = [10, 20, 12, 55, 30]
for age in ages where age > 20 {
print(age)
}
// 输出:55 30
示例代码三(protocol
):
protocol Stackable {
associatedtype Element
}
protocol Container {
associatedtype Stack : Stackable where Stack.Element : Equatable
}
示例代码四(func
):
func equal<S1: Stackable, S2: Stackable>(_ s1: S1, _ s2: S2) -> Bool where S1.Element == S2.Element, S1.Element : Hashable {
return false
}
示例代码五(extension
):
extension Container where Self.Stack.Element : Hashable {
}