002-Swift2.0

一、基本类型

1.常量、变量和声明

// Swift是强类型语言,特别智能,可以自动地根据声明时的赋值获取变量类型
// 所以在声明常量或变量时可以不进行类型声明

// 常量(用let声明)
let maxNum1 = 3000
let maxNum2: Int = 2000
let maxNum3: Float = 20
let maxNum4: Float = 20.001
// 声明常量时没有赋初值,一定要先指明常量类型,后面的赋值不被称为改变常量的值,而是作为第一次赋值,但是不可以进行二次赋值
let mm = 20
let qq: Int
if mm == 20 {
    qq = 2000
}
else {
    qq = 1000
}
// 一次性声明多个常量
let x1 = 1, y1 = 2.01, z1 = "Hello"
let a1: Int, b1: String, c1: Float
a1 = 90
b1 = "Frank"
c1 = 30.22
let r1, s1, t1: Int
r1 = 20
s1 = 30
t1 = 50

// 变量(用var声明)
var minNum1 = 2000
var minNum2: Int = 2000
var minNum3: Float = 20
var minNum4: Float = 20.001
// 变量可以改变值
minNum1 = 10
minNum2 = 20
minNum3 = 20.0122
minNum4 = 24.98
// 一次性声明多个变量
var x2 = 1, y2 = 2, z2 = 3
var a2: Int, b2: String, c2: Float
a2 = 90
b2 = "Frank"
c2 = 30.22
var r2, s2, t2: String
r2 = "Frank"
s2 = "冬哥"
t2 = "👿"

// 另外不管是常量,还是变量,在赋初值之前不可以被使用,否则编译器报错

2.常用数据类型

1)Int(整型)

// 1.获取整型Int的最值
Int.max
Int.min

// 2.Swift提供的整型
// 无符号整型 UInt
// 8位:Int8、UInt8
// 16位:Int16、UInt16
// 32位:Int16、UInt16
// 64位:Int64、UInt64

// 3.不同的进制给变量赋值
let decimalInt: Int = 17
let binaryInt: Int = 0b10001
let octalInt: Int = 0o21
let hexInt: Int = 0x1A

// 4.下划线分割整型,便于读写,不影响数值
let monery: Int = 100_0000_0000

2)Float(32位)与Double(64位)(浮点型)

// 1.浮点数分单精度浮点类型和双精度浮点类型
// Float:单精度浮点类型,32位
// Double:双精度浮点类型,64位
let imFloat: Float = 3.1415926
let imDouble: Double = 3.1415926

// 2.不指定类型,默认为Float类型
let w = 3.1415926
var a = 1.25e10
var b = 1.25e-8

// 3.下划线分割,便于读写,不影响数值
var c = 1_0000.000_000_1

// 4.类型转换
// 对于Swift语言,它不存在自动强转的说法
// 所以我们如果对不同类型的变量进行运算而不进行手动强转,程序编译就会出错
// 这个也表明Swift是安全性语言
let x: UInt16 = 100
let y: UInt32 = 20
// x + y (报错)
let m = x + UInt16(y)
// 浮点类型之间的转换
let aa: Double = 3.0
let bb: Float = 0.3
aa + Double(bb)
Float(aa) + bb
// 可以将整数声明成浮点类型,但不可以反过来
let xx: Float = 3
// let yy: Int = 3.0 (报错)
let integer = 3
let fraction = 0.1415926
// integer + fraction (报错)

// 但是有个问题,我们在进行一个类型强转到另一个数据类型,要特别注意数据溢出,数据溢出,编译器就会报错,这也说明Swift是安全性语言
// 比如:UInt64强转UInt8等等,需要注意数据溢出

// 5.CGFloat
// 不可以用Float来替代CGFloat
let red: CGFloat = 0.2
let green: CGFloat = 0.2
let blue: CGFloat = 0.2
UIColor.init(red: red, green: green, blue: blue, alpha: 1.0)

3)Bool(布尔类型)

// 在Swift中不可以用0和1来分别代表false和ture
let imTrue: Bool = true
let imFalse: Bool = false
if imTrue {
    print("I'm ture")
}
else if 3 + 4 == 7 {
    print("3 + 4 == 7")
}
else {
    print("I'm false")
}

4)Tuple(元祖类型)

// 1.元祖之间以逗号隔开
var point1 = (5, 2)
var httpResponse1 = (404, "Not Found")

// 2.元祖指定元素类型的声明
var point2: (Int, Int, Int) = (10, 5, 1)
var httpResponse2: (Int, String) = (200, "OK")

// 3.元祖指定元素名的声明
let point3 = (x: 3, y: 2)
point3.x
point3.y

// 4.元祖指定元素类型时指定元素名的声明
var point4: (x: Int, y: Int, z: Int) = (10, 5, 1)
point4.x
point4.y
point4.z
point4.0
point4.1
point4.2

// 5.元祖解包
var point = (6, 9)
var (x, y) = point // 这个里面的x和y都是变量,值可变
let (a, b) = point // 这个里面的a和b都是常量,值不可变
let s = point.0
var t = point.1
// 解包元祖中的部分元素(在Swift中下划线“_”有忽略的意思)
let loginResult = (true, "Frank")
let (isLoginSuccess, _) = loginResult
if isLoginSuccess {
    print("Login success")
}
else {
    print("Login failed")
}

3.变量名、print、注释

1)Swift中变量名可以是中文,也可以是表情符号

var 名字 = "Frank"
var 😂 = "crying"

2)print打印

// 1.可以直接将两个参数进行相加打印输出
var 名字 = "Frank"
var 😂 = "crying"
print(名字 + 😂)
let x = 1, y = 2, z = 3, b = true
print(x + y)

// 2.任何基本数据类型变量都可以作为参数直接放在print中打印输出
let x1 = 1, y1 = 2, z1 = 3, b1 = true
// 打印输出分隔符默认为空格
print(x1, y1, z1, b1)
// separator设置打印输出分隔符(默认为空格)
print(x1, y1, z1, b1, separator: "--@")
// terminator设置打印输出结束符(默认为回车换行)
print(x1, y1, z1, b1, separator: "--@", terminator: "~~")

// 3.运算输出(9 * 8 = 72)
let x2 = 9, y2 = 8
// 常规
print(x2, "*", y2, "=", x2*y2)
// 字符串中采用\(),字符串中\(a)表示a的值
print("\(x2) * \(y2) = \(x2*y2)")

 3)注释

  // 单行注释

  /* 多行注释 */

  /*  支持嵌套注释    /* 嵌套注释 */ */

 

二、基础运算符

1.赋值运算符

  =

2.算数运算符

  a + b        a += 2

  a - b         a -= 2

  a * b         a *= 2

  a / b        a /= 2

  a % b      a %= 2

  特别说明,在Swift中可以对浮点类型进行求余,例如:2.5 % 1.2 = 0.1

3.单目运算符

特别说明:单目运算符和操作数不可以分开

例:let x = 3

  var xx = +x    // 不能写成:var xx = + x

  var yy = -x     // 不能写成:var yy = - x

  !a                   // 不能写成:! a

  !(a + b)           // 不能写成:! (a + b)

4.比较运算符

  a == b     a > b      a < b

  a != b  a >= b    a <= b

  a === b   a !== b

5.逻辑运算符

  !a

  a && b

  a || b

6.三目运算符

  a ? b : c

// 1.if esle语句
var battery1 = 21
let batteryColor1: UIColor    // 这里没有对常量batteryColor1进行赋值
// 这里的if - else才对batteryColor1进行第一次赋值
if battery1 <= 20 { batteryColor1 = UIColor.redColor() } else { batteryColor1 = UIColor.blueColor() } // 2.三目运算符 let battery2 = 21 let batteryColor2 = battery2 <= 20 ? UIColor.redColor() : UIColor.blueColor()

7.区间运算符 

1)闭区间运算符

  a...b 表示 [a, b]

2)前开后闭运算符

  a..<b 表示[a, b)

// 1...10 表示 [1, 10]
for index in 1...10 {
    index
}

// 0..<10 表示 [0, 10)
for index in 0..<10 {
    index
}

let names = ["Frank", "Baochun", "Zhangxi", "Chenbo", "Jinkai"]
for index in 0..<names.count {
    print(names[index])
}

 

三、逻辑控制

1.程序结构

1)顺序结构:程序从上往下一行一行地按顺序执行

2)循环结构:for-in循环、for循环、while循环、do-while循环等(注意:在Swift中do-while语句换成了repeat-while)

3)选择结构:if-else、if-else if、switch等(注意:在swift中对switch语句有了很多加强)

  另外,在循环结构中,循环体语句不管多少行,哪怕只有一行;在选择结构中,选择语句不管有多少行,哪怕只有一行,不同于其他语言的一点就是必须带上大括号{}

2.循环结构

1)for-in与for

for-in:在for-in语句中不需要去声明变量i,系统自动声明为常量let类型;另外由于i为let类型常量,所以我们不能够去改变它的值

for:在for语句中需要去声明变量i,一般会声明为变量var类型;另外for语句在Swift后面的版本中会被弃用

// 1.for-in语句
// 1)不忽略角标
// 注意:在for-in语句中不需要去声明变量i,系统自动声明为常量let类型
for i in -99...99 {
    i * i
    if i == 0 {
    
    }
}
// 2)忽略角标(采用下划线)
var result = 1
var base = 2
var power = 10
for _ in 1...power {
    result *= base
}

// 2.for语句
// 注意:for循环在Swift3.0版本中会被弃用
// 注意:在for语句中需要去声明变量i,一般会声明为变量var类型
for var i = -99; i <= 99; i+=1 {
    i * i
}

2)while与repeat-while

// 1.while语句
var aWin = 0 // A连续胜利的次数
var bWin = 0 // B连续胜利的次数
var game = 0 // 比赛的次数
while aWin < 3 && bWin < 3 {
    game += 1
    
    let a = arc4random_uniform(6) + 1
    let b = arc4random_uniform(6) + 1
    
    if a > b {
        bWin = 0
        aWin += 1
        print("A win!")
    }
    else if a < b {
        aWin = 0
        bWin += 1
        print("B win!")
    }
    else {
        aWin = 0
        bWin = 0
        print("draw")
    }
}
let winner1 = aWin == 3 ? "A" : "B"
print("After \(game) games, \(winner1) win!")

// 2.repeat-while语句
// 注意:在Swift语句中do-while语句已经被换成了repeat-while
var xWin = false
var yWin = false
repeat {
    let x = arc4random_uniform(6) + 1
    let y = arc4random_uniform(6) + 1
    
    if x > y {
        xWin = true
    }
    else if x < y {
        yWin = true
    }
    
} while !xWin && !yWin
let winner2 = xWin ? "X" : "Y"
print("\(winner2) win!")

3.选择结构

1)if-else if-esle

  用法与OC一致

2)switch

a.区别

  swift中不需要在每一个case末尾加一个break,因为在swift语句中执行完一个case会直接跳转结束

  swift语句中不允许case后面什么都不写;如果某一个case不需要作任何操作,我们可以加关键字break,强制退出;或者跟一对小括号(),表示空语句

  swift中如果需要执行多个值的case,我们可以将这些值写在同一个case中,以逗号隔开

  swift中对于case的值可以为任何基础数据类型值

  swift中必须穷举变量的所有值,所以如果在case中可以穷举变量的值,我们也可以省略Default  

  swift中如果需要执行完一个case,继续执行下一个case,那么可以使用关键字fallthrough

b.基本用法

// 1.不需要在每一个case末尾加一个break
// 执行完一个case会自动结束
let score1 = "A"
switch score1 {
    case "A":
        print("优秀")
    case "B":
        print("良好")
    case "C":
        print("一般")
    case "D":
        print("及格")
    default:
        print("不及格")
}

// 2.不允许case后面什么都不写
// 如果某一个case不需要作任何操作,可以采用下面两个方法:
// 1)关键字break,强制退出
let score2 = "E"
switch score2 {
    case "A":
        print("优秀")
    case "B":
        print("良好")
    case "C":
        print("一般")
    case "D":
        print("及格")
    default:
        break
}
// 2)跟一对小括号(),表示空语句
let score3 = "D"
switch score3 {
    case "A":
        print("优秀")
    case "B":
        print("良好")
    case "C":
        print("一般")
    case "D":
        ()
    default:
        ()
}

// 3.如果需要执行多个值的case
// 我们可以将这些值写在同一个case中,以逗号隔开
let score4 = "D"
switch score4 {
    case "A", "B", "C", "D":
        print("及格")
    default:
        print("不及格")
}

// 4.对于case的值可以为任何基础数据类型值
let score5 = 50
switch score5 {
    case 90...100:
        print("优秀")
    case 80..<90:
        print("良好")
    case 70..<80:
        print("一般")
    case 60..<70:
        print("及格")
    default:
        print("不及格")
}

// 5.必须穷举变量的所有值
// 所以如果在case中可以穷举变量的值,我们也可以省略Default
let score6 = true
switch score6 {
    case true:
        print("及格")
    case false:
        print("不及格")
}

// 6.执行完一个case后,使用关键字fallthrough跳转下一个case
let score7 = 90
switch score7 {
    case 90...100:
        print("优秀")
        fallthrough 
    case 60...100:
        print("及格")
    case 0..<60:
        print("不及格")
    default:
        print("缺考")
}

c.高级用法

// 1.switch中的区间运算符
let score = 50
switch score {
    case 90...100:
        print("优秀")
    case 80..<90:
        print("良好")
    case 70..<80:
        print("一般")
    case 60..<70:
        print("及格")
    default:
        print("不及格")
}

// 2.switch中的元祖
// 1)纯元祖
let point1 = (1, 1)
switch point1 {
    case (0, 0):
        print("当前点在坐标原点上")
    case (1, 0):
        print("当前点在X轴上值为1的地方")
    case (0, 1):
        print("当前点在Y轴上值为1的地方")
    default:
        print("当前点在其他位置");
}
// 2)元祖与下划线结合
let point2 = (1, 1)
switch point2 {
    case (0, 0):
        print("当前点在坐标原点上")
    case (_, 0): // 下划线可以忽略元祖的一些值
        print("当前点在X轴上")
    case (0, _):
        print("当前点在Y轴上")
    default:
        print("当前点在象限区域内");
}
// 3)元祖与区间运算符结合
let point3 = (1, 0.2)
switch point3 {
    case (0, 0):
        print("当前点在坐标原点上")
    case (_, 0):
        print("当前点在X轴上")
    case (0, _):
        print("当前点在Y轴上")
    case (-1...1, -1...1): // 区间判断
        print("当前点在以原点为圆心,半径为1的圆形区域内")
    default:
        print("当前点在象限区域内");
}
// 4)元祖赋值
let point4 = (0, 0)
switch point4 {
    case (0, 0):
        print("当前点在坐标原点上")
        fallthrough
    case (_, 0):
        print("当前点在X轴上")
    case (0, _):
        print("当前点在Y轴上")
    case (-1...1, -1...1):
        print("当前点在以原点为圆心,半径为1的圆形区域内")
    case (let x, let y):  // 元祖赋值
        print("当前点横坐标为\(x),纵坐标为\(y)")
}

4.跳转控制关键字

1)break与continue

  一般用法同于OC中的用法,不同的是我们可以在swift中给代码作标签,然后可以通过“break + 标签名”的形式去跳转出相应的代码块

// 1.break的一般用法(这里的break只是跳出最里面的那层for循环)
for m in 1...300 {
    for n in 1...300 {
        if m * m * m * m - n * n == 15 * m * n {
            print(m, n)
            break
        }
    }
}

// 2.break+标签的用法(跳转出指定标签的那层for循环)
findAnswer:
for m in 1...300 {
    for n in 1...300 {
        if m * m * m * m - n * n == 15 * m * n {
            print(m, n)
            break findAnswer
        }
    }
}

2)fallthrough

  主要用于switch语句中,用于执行多个case

5.where关键字(后面接限制条件)

  where主要是跟着case在使用

// 1.switch语句中的case where
let point = (3, 3)
switch point {
    case let (x, y) where x == y:
        print("It's on the line x == y!")
    case let (x, y) where x == -y:
        print("It's on the line x == -y!")
    case let (x, y):
        print("The point is (\(x), \(y))")
}

// 2.if语句中的case where
// 注意:if case在一起使用,变量必须跟在case的后面
let age = 19
if case 10...19 = age {
    print("You are a teenager.")
}
// 注意:这里的where也只是跟着case配套的,跟if没有关系
if case 10...19 = age where age >= 18 {
    print("You are a teenager and in a college.")
}
let vector = (4, 0)
if case (let x, 0) = vector where x > 2 && x < 5 {
    print("It's the vector!")
}

// 3.for循环中的case where
// 1)普通的for循环(找出1到100之间的能被3整除的数)
for i in 1...100 {
    if i % 3 == 0 {
        print(i)
    }
}
// 2)case where(找出1到100之间的能被3整除的数)
for case let i in 1...100 where i % 3 == 0 {
    print(i)
}

6.guard关键字(确保满足条件)

  guard一般与else一起使用

// 1.不使用guard的函数
func buy1(money: Int, price: Int, capacity: Int, volume: Int) {
    if money >= price {
        if capacity >= volume {
            print("I can buy it!")
            print("\(money-price) Yuan left.")
            print("\(capacity-volume) cubic meters left")
        }
        else {
            print("No enough capacity")
        }
    }
    else {
        print("Not enough money")
    }
}
// 2.使用guard的函数(推荐使用) // guard:确保 // 结构:先进行边界判断,然后再进行核心的代码(推荐使用这种代码风格) func buy2(money: Int, price: Int, capacity: Int, volume: Int) { guard money >= price else { print("Not enough money") return } guard capacity >= volume else { print("No enough capacity") return } print("I can buy it!") print("\(money-price) Yuan left.") print("\(capacity-volume) cubic meters left") }

 

四、字符串String

// 1.初始化方法
// 1)非空字符串
var str1: String = "Hello playground"
let str2 = String("Hello playground")
var str3 = "Hello playground"
// 2)空字符串
var emptyString1 = ""
var emptyString2 = String()

// 2.判断字符串是否为空
emptyString1.isEmpty

// 3.运算符+和\()在字符串中的使用
// 1)+在字符串中的使用
let mark = "!!!"
str1 + mark  // 这里是重新生成一个新的字符串
str1 += mark // 这里是在str1后面直接追加,所以str1必须是变量
// 2)\()在字符串中的使用
let name = "Frank"
let age = 25
let work = "iOS"
print("My name is \(name). I'm \(age) year old. And my job is to \(work) software engineer")

// 4.遍历字符串
var str = "Hello, playground"
for c in str.characters {
    print(c)
}

// 5.字符(Character)
let markCharacter: Character = "!"
// 在字符串后面追加字符(注意这个方法只能是变量的字符串类型)
str.append(markCharacter)
let englishLetter: Character = "a"
let chineseLetter: Character = ""
let dog: Character = "🐶"
let coolGuy: Character = "\u{1F60E}"
// 注意:字符串和字符都是使用的双引号"",所以在声明字符时必须指明字符类型
// 因为在Swift中默认声明的是String类型
// 另外,由于字符串和字符类型不停,所以这两中类型的变量是不可以直接相加的

// 6.字符串的长度
// 1)英文字符串
var coolLetters = "abc"
coolLetters.characters.count
// 2)中文字符串
var chineseLetters = "慕课网"
chineseLetters.characters.count
// 3)Unicode字符串
var coolGuys = "\u{1F60E}\u{1F60E}\u{1F60E}\u{1F60E}"
coolGuys.characters.count
// 4)Swift中字符串的智能识别
var cafe1 = "café"
var cafe2 = "cafe\u{0301}"
cafe1.characters.count
cafe2.characters.count
print(cafe1)
print(cafe2)
cafe1 == cafe2

// 7.索引(Index)
var string1 = "Hello Swift"
// 指向开始字符的索引
let startIndex = string1.startIndex
// 指向结束字符的索引(注意:endIndex不是最后一个字符的位置)
let endIndex = string1.endIndex
// 获得距离当前索引n的索引(这里的n=4)
let index = startIndex.advancedBy(4)
// 获得当前索引的前一个索引
let predecessorIndex = index.predecessor()
// 获得当前索引的后一个索引
let successorIndex = index.successor()
// 获得当前索引所对应的字符
string1[index]
// 总结:索引startIndex和endIndex构成了一个前闭后开的区间,这个区间就是字符串的字符区间;也就是说,索引endIndex不是字符串最后一个字符的索引;所以在使用时我们要防止索引越界

// 8.索引范围(Range)
var string2 = "My name's Frank! I'm 25 year old!"
let index1 = string2.startIndex.successor()
let index2 = index1.advancedBy(6)
let range = index1..<index2
// 根据索引范围(Range)获取子串
let subString = string2[range]
// 根据索引范围(Range)替换字符串
string2.replaceRange(range, with: "替换字符串")
// 字符串末尾追加字符串(母串被改变)
string2.appendContentsOf("追加字符串")
// 字符串末尾追加字符串(母串不被改变,重新生成新的字符串)
string2.stringByAppendingString("追加字符串")
// 在字符串指定索引(Index)位置添加字符
string2.insert("@", atIndex: index2)
// 删除某一索引范围(Rnage)的字符串
string2.removeRange(range)
// 删除某一索引(Index)的字符
string2.removeAtIndex(index1)
// 删除所有字符
string2.removeAll()

// 9.大小写转换
var string3 = "My name's Frank! I'm 25 year old!"
// 字符串转大写
string3.uppercaseString
// 字符串转小写
string3.lowercaseString
// 字符串首字母转大写
string3.capitalizedString

// 10.子串判断
var string4 = "http://baidu.com"
// 判断字符串是否包含某一个子串
string4.containsString("baidu")
// 判断字符串的前缀
string4.hasPrefix("http")
string4.hasSuffix("com")

// 11.as关键字
let s1 = "one third is \(1.0 / 3.0)"
// NSString --> String
let s2: String = NSString(format:"one third is %.2f", 1.0 / 3.0) as String
// 目前Swift中String类型的方法不是很多,所以我们可以对String类型使用as关键字,将它当成NSString类型,然后通过NSString类型的方法来现在我们需要的功能,最后将NSString转化为String类型
var s3: NSString = "one third is 0.33"
s3.substringFromIndex(4)
s3.substringToIndex(3)
s3.substringWithRange(NSMakeRange(3, 2))
// 去掉不相干字符
let s4 = "----------  --- Hello ----- " as NSString
s4.stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString:" -"))

 

、可选型(Optional)

1.可选型

 引进目的:在Swift中用nil表示没有,表示空,但是变量又不可以直接赋值为nil,这就引进了可选型

 用法:声明变量时,在变量类型后面直接跟问号,就表示当前类型的可选型

 注意事项:

  1.可选型必须显性声明

  2.虽然可选型可声明为let类型,但是可选性是需要改变值的,所以一般声明为var类型

  3.可选型是不可以直接使用的,防止野指针错误

2.基本使用

// Int? 整型可选型
var errorCode1: Int? = 404
errorCode1 = 0
errorCode1 = nil
// String? 字符串可选型
var errorCode2: String? = "404"
errorCode2 = nil

3.可选型的解包

// 1.if else判断
var errorCode02: String? = "404"
if errorCode02 != nil {
    print("The errorCode is " + errorCode02!)
}
else {
    print("No error")
}

// 2.强制解包(!)
// 注意:强制解包只有在确定errorCode01不为nil的时候才使用;如果为nil,解包就会失败,程序报错
var errorCode01: String? = "404"
let errorCode001 = errorCode01! // 强制解包
print("The errorCode is " + errorCode001)

// 3.解包(解包操作if的判断条件)
// 1)解包单个可选型变量
var errorCode03: String? = "404"
if let errorCode03 = errorCode03 {
    print("The errorCode is " + errorCode03)
}
// 2)解包多个可选型变量
var errorCode04: String? = "404"
var errorMessage04: String? = "Not Found!"
if let errorCode04 = errorCode04, errorMessage04 = errorMessage04 {
    print("The errorCode is " + errorCode04 + "\nThe errorMessage is " + errorMessage04)
}
// 3)解包中的where关键字
var errorCode05: String? = "404"
var errorMessage05: String? = "Not Found!"
if let errorMessage05 = errorMessage05 where errorCode05 == "404" {
    print("Page Not Found!")
}
// 总结:
// a.这个是将可选型的解包操作作为判断条件,如果解包成功,则进入if语句;如果解包失败,则不进入
// b.可选型解包后的结果为可选型的原本类型
// c.重新声明一个变量来接收解包后的可选型,该变量名可以与可选型变量名一致

// 4.尝试解包(?)
// 对可选型进行尝试解包,由于解包成功与否的不确定性,所以尝试解包后的结果同样也是可选型
var errorMessage06: String? = "Not Found"
var uppercaseErrorMessage = errorMessage06?.uppercaseString
// 3)将尝试解包操作作为判断条件,那么解包的结果就是可选型的原类型
// 对可选型的尝试解包结果再进行解包
var errorMessage07: String? = "Not Found"
if let uppercaseErrorMessage = errorMessage07?.uppercaseString {
    uppercaseErrorMessage
}
// 总结:
// a.对于一些可选型的原类型方法,可选型是不可以直接调用原类型的方法的,必须进行解包才能调用
// b.对于一些可选型进行尝试解包后,对尝试解包的结果再进行解包,结果为可选型的原类型

// 5.获取String可选型的原类型结果(String)
// 1)通过三目运算符
var errorMessage1: String? = nil
// 这个还是获取的是可选型
let message = errorMessage1 != nil ? errorMessage1 : "No Error!"
// 下面可以使用强制解包获取String类型
let message1 = message!
// 2)通过解包
var errorMessage2: String? = nil
let message2: String
if let errorMessage2 = errorMessage2 {
    message2 = errorMessage2
}
else {
    message2 = "No Error!"
}
// 3)通过??
var errorMessage3: String? = nil
let message3 = errorMessage3 ?? "No Error!"

4.元祖中的可选型

// 1)部分元素为可选型
var error1: (errorCode: Int, errorMessage: String?) = (404, "No Found!")
error1.errorMessage = nil
// 2)整个元祖为可选型
var error2: (errorCode: Int, errorMessage: String)? = (404, "Not Found!")
error2 = nil
// 3)部分元素和整个元祖为可选型
var error3: (errorCode: Int, errorMessage: String?)? = (404, "Not Found!")
error3?.errorMessage = nil
error3 = nil

5.可选型的实际应用

var ageInput: String = "16"
// age为Int的可选型
var age = Int(ageInput)
if  let age = age where age < 20 {
    print("You're a teenager!")
}

var greetings = "Hello"
// rangeOfString返回的是可选型
greetings.rangeOfString("oo")

6.隐式可选型

 在声明的时候可以声明为nil,在使用的时候可以不进行解包;但是这个比较危险;当为nil时,我们直接使用就会报错

 一般用于类的构造方法中

var errorMessage: String! = nil
errorMessage = "No Found!"
// 隐式可选型可以直接使用,不需要解包,但是是不安全的
"The message is " + errorMessage

 

六、数组(Array)

1.数组的创建

// 1)不指明类型(不建议这么创建)
var numbers1 = [0, 1, 2, 3, 4, 5]
var vowels1 = ["A", "E", "I", "O", "U"]
// 2)指明声明类型
var numbers2: [Int] = [0, 1, 2, 3, 4, 5]
var vowels2: [String] = ["A", "E", "I", "O", "U"]
var character2: [Character] = ["A", "E", "I", "O", "U"]
// 3)使用泛型的方式
var numbers3: Array<Int> = [0, 1, 2, 3, 4, 5]
var vowels3: Array<String> = ["A", "E", "I", "O", "U"]
var character3: Array<Character> = ["A", "E", "I", "O", "U"]
// 4)声明空数组
var emptyArray1: [Int] = []
var emptyArray2: Array<Int> = []
var emptyArray3 = [Int]()
var emptyArray4 = Array<Int>()
// 5)通过方法创建
var allZeros1 = [Int](count:5, repeatedValue:0)
var allZeros2 = Array<Int>(count:5, repeatedValue:1)

2.数组的基本操作

// 1.数组的长度
var numbers1 = [1, 2, 3, 4, 5]
numbers1.count

// 2.判断数组是否为空
var numbers2 = [1, 2, 3, 4, 5]
numbers2.isEmpty

// 3.获取数组中元素
// 1)根据下标
var numbers3 = [1, 2, 3, 4, 5]
numbers3[0]
numbers3[1]
// 2)获取首元素
var numbers4 = ["1", "2", "3", "4", "5"]
let firstNumber = numbers4.first // 结果为可选型,因为数组不一定存在首元素
// 解包
if let firstNumber = firstNumber {
    print("The first number is " + firstNumber)
}
// 3)获取最后一个元素
var numbers5 = ["1", "2", "3", "4", "5"]
let lastNumber = numbers5.last // 结果为可选型,因为数组不一定存在最后一个元素
// 解包
if let lastNumber = lastNumber {
    print("The last number is " + lastNumber)
}
// 4)获取数组中最值元素
var numbers6 = ["1", "2", "3", "4", "5"]
// 结果为可选型,因为不一定存在
numbers6.minElement() // 最小值元素
numbers6.maxElement() // 最大值元素
// 5)获取一定索引范围的子数组
var numbers7 = ["1", "2", "3", "4", "5"]
numbers7[2..<4]
numbers7[2..<numbers7.count]

// 4.判断数组中是否包含某一个元素
var numbers8 = ["1", "2", "3", "4", "5"]
numbers8.contains("1")
numbers8.contains("A")

// 5.判断元素是否包含于数组中
// 如果存在,返回的是当前元素在数组中的索引位置
// 如果不存在,返回的就是nil
// 由此可见,结果返回的是可选型
var numbers9 = ["1", "2", "3", "4", "5"]
numbers9.indexOf("3")
numbers9.indexOf("A")

// 6.遍历数组
// 1)通过索引遍历(不推荐)
var numbers10 = ["1", "2", "3", "4", "5"]
for index in 0..<numbers10.count {
    numbers10[index]
}
// 2)直接通过元素遍历
for number in numbers10 {
    print(number)
}
// 3)通过元祖去遍历,同时获取索引和元素(推荐)
var numbers11 = ["1", "2", "3", "4", "5"]
for (index, number) in numbers11.enumerate() {
    print("第\(index+1)个元素是:\(number)")
}

// 7.判断数组是否相等
var vowels1 = ["A", "E", "I", "O", "U"]
var vowels2 = ["E", "I", "A", "O", "U"]
vowels1 == vowels2

3.数组的增删改

// 1.增
// 1)向数组中追加一个元素
var courses1 = ["Frank1", "Frank2", "Frank3"]
courses1.append("Frank4")
// 2)向数组中追加一个数组的元素
var courses2 = ["Frank1", "Frank2", "Frank3"]
courses2 += ["22", "33"]
courses2 = courses2 + ["22", "33"]
// 3)插入
var courses3 = ["Frank1", "Frank2", "Frank3"]
courses3.insert("Frank0", atIndex: 0)

// 2.删
var numbers = ["1", "2", "3", "4", "5", "6"]
// 删除首元素
numbers.removeFirst()
// 删除最后一个元素
numbers.removeLast()
// 删除某个索引位置的元素
numbers.removeAtIndex(2)
// 删除某个范围的元素
numbers.removeRange(0..<2)
// 删除所有元素
numbers.removeAll()

// 3.改
var names = ["Frank1", "Frank2", "Frank3", "Frank4", "Frank5", "Frank6"]
// 修改某一个元素的值
names[0] = "FrankXXX"
// 修改某一个索引范围的数组元素值(两个数组元素的个数可以不相等)
names[0..<2] = ["11", "22", "33", "44", "55"]

4.二维数组

// 1.二维数组的创建
var board: Array<Array<Int>> = [[1026, 0, 2], [26, 12, 278], [6, 67, 200], [1026, 0, 2]]

// 2.获取二维数组中的元素
// 1)获取二维数组中某一行的元素(依然是个数组)
board[0]
// 2)获取二维数组中某行某列的元素
board[1][1]

// 3.获取二维数组的长度
// 1)获取整个二维数组的外层长度
board.count
// 2)获取二维数组中某一行元素的长度(依然是数组的长度)
board[0].count

// 4.追加
// 给二维数组中某一行元素追加单个元素
board[0].append(0)
// 给二维数组追加元素(此时追加的元素必须为数组)
board.append([0, 0, 0])

// 5.二维数组的加法运算
// 1)对整个二维数组进行加法运算(此时必须同为二维数组)
board += [[0, 0, 0, 0]]
// 2)对二维数组中某一个元素进行加法运算
board[0] += [0, 0, 0, 0]

5.NSArray

// 空数组,同时不声明类型,默认为NSArray,不建议这么搞
var array1 = []
// as:Array转化为NSArray
var array2 = [1, 2, 3, 4] as NSArray
// 显式声明
var array3: NSArray = [1, "Hello", 3.0]

// 对于swift语言的数组是不能承载不同的数据类型
// 这个数组承载的元素的数据类型是不一致的,所以默认就是它的所有元素都是NSObject类型
var array4 = [1, "Hello", 3.0]
// 一般我们建议显式声明
var array5: [NSObject] = [1, "Hello", 3.0]

 

七、字典(Dictionary)和集合(Set)

1.字典(Dictionary)

// 1.字典的声明
// 键和值都是String类型的第一种声明
var dict1: [String : String] = ["swift": "雨燕", "python": "大蟒"]
// 键和值都是String类型的第二种声明
var dict2: Dictionary<String, String> =  ["swift": "雨燕", "python": "大蟒"]
// 空的字典
var emptyDictionary1: [String : Int] = [ : ]
var emptyDictionary2: Dictionary<Int, String> = [ : ]
var emptyDictionary3 = [String : String]()
var emptyDictionary4 = Dictionary<Int, Int>()

// 2.字典的属性
// 1)根据键取值(值为可选型,因为字典中很有可能不存在该键)
dict1["swift"]
if let value = dict1["swift"] {
    print("swift的意思是:\(value)")
}
// 2)字典长度
dict1.count
// 3)判断字典是否为空
dict1.isEmpty
// 4)获取字典所有的键值(强制转化为数组)
Array(dict1.keys)
Array(dict1.values)
// 5)遍历字典所有的键值
for key in dict1.keys {
    print(key)
}
for value in dict1.values {
    print(value)
}
for (key, value) in dict1 {
    print("\(key) : \(value)")
}

// 3.字典的一些特性
// 1)字典是无序的
let dict11 = [1: "A", 2: "B"]
let dict12 = [2: "B", 1: "A"]
dict11 == dict12
// 2)字典中键是唯一的,值可以不唯一

2.字典的操作

// 1.改
var user1 = ["name": "Frank", "password": "dd", "age": "25"]
// 1)直接通过键来改(无返回值)
user1["name"] = "Frank Li"
// 2)通过方法去修改(返回该键的原始值,返回值为可选型:有可能不存在该键)
let oldPassword = user1.updateValue("ll", forKey: "password")
if let oldPassword = oldPassword, newPassword = user["password"] where oldPassword == newPassword {
    print("注意:修改之后的密码和之前的一样");
}

// 2.增
var user2 = ["name": "Frank", "password": "dd", "age": "25"]
// 1)通过键赋值,没有该键就是增加这个键值对
user2["email"] = "10588@qq.com"
// 2)通过方法去增加
user2.updateValue("东风一小", forKey: "school")

// 3.删
var user3 = ["name": "Frank", "password": "dd", "age": "25"]
// 1)通过键去删除指定的键值对
user3["school"] = nil
// 2)通过方法去删除指定的键值对(返回值为可选型:有可能不存在该键)
user3.removeValueForKey("name")
if let email = user3.removeValueForKey("email") {
    print("电子邮件\(email)删除成功")
}
// 3)删除字典中所有的键值对
user3.removeAll()

// 4.查
var user4 = ["name": "Frank", "password": "dd", "age": "25"]
// 直接通过键去查看当前元素的值(可选型:有可能不存在该键)
var x = user4["name"]
if let x = x {
    print(x)
}

3.集合(Set)

// 1.集合的声明
var skillsOfA: Set<String> = ["swift", "OC"]
var skillsOfB: Set<String> = ["swift", "OC", "OC"]
var vowels1 = Set(["A", "B", "C"])
var vowels2 = Set<String>(["A", "B", "C"])
var vowels3: Set = ["A", "B", "C"]
// 空集合
var emptySet1: Set<Int> = []
var emptySet2 = Set<Double>()

// 2.集合的属性
// 1)集合的长度
let set: Set<Int> = [2, 2, 2, 2]
set.count
// 2)判断集合是否为空
set.isEmpty
// 3)随机取出一个元素
let e = set.first
// 4)判断是否包含某一个元素
set.contains(3)
// 5)集合的遍历
for skill in skillsOfA {
    print(skill)
}

// 3.集合的一些特性
// 集合无序、值唯一
let setA: Set = [1, 2, 3]
let setB: Set = [1, 3, 2, 3, 1, 1, 1, 1]
setA == setB

4.集合的操作

var skillsOfX: Set<String> = ["swift", "OC"]
var skillsOfY: Set<String> = ["HTML", "CSS", "Javascript"]
var skillsOfZ: Set<String> = []
var skillsOfR = Set(["swift", "OC"])
var skillsOfS = Set(["HTML", "CSS", "Javascript"])
var skillsOfT = Set(["swift", "HTML", "CSS"])

// 1.插入
skillsOfZ.insert("swift")
skillsOfZ.insert("HTML")
skillsOfZ.insert("CSS")
// 已经存在的元素,重复插入,不会影响原集合的值
skillsOfZ.insert("CSS")

// 2.删除
// 1)删除指定元素(返回值为被删除的元素,类型为可选型:因为有可能不存在要删除的元素)
skillsOfZ.remove("Javascript") // 删除不存在的元素,不会报错,返回nil
if let skill = skillsOfZ.remove("CSS") {
    print("\(skill)被删除")
}
// 2)随机删除一个元素
skillsOfZ.removeFirst()
// 3)删除所有的元素
skillsOfZ.removeAll()

// 3.集合的并集
// union:不改变原有的集合
// unionInPlace:改变原有的第一个集合
var a = skillsOfR.union(skillsOfS) // skillsOfR和skillsOfS本身不发生变化
skillsOfR.unionInPlace(skillsOfS) // skillsOfR发生变化

// 4.集合的交集
// intersect:不改变原有的集合
// intersectInPlace:改变原有的第一个集合
var b = skillsOfR.intersect(skillsOfS)
skillsOfR.intersectInPlace(skillsOfS)

// 5.集合的减法
// subtract:不改变原有的集合
// subtractInPlace:改变原有的第一个集合
var c = skillsOfR.subtract(skillsOfS)
skillsOfR.subtractInPlace(skillsOfS)

// 6.集合的异或
// exclusiveOr:集合A中不存在B集合的元素集
skillsOfR.exclusiveOr(skillsOfS)
// 注意:后面的一个参数都可以是一个数组
var d = skillsOfR.union(["java", "OC"])

// 7.子集和真子集
// isSubsetOf:子集(A是B的子集,表示B中包含A的所有元素,其中包括B==A)
// isStrictSubsetOf:真子集(A是B的真子集,表示B中包含A的所有元素,但是B!=A)
var p: Set = ["a"]
var q: Set = ["b", "c", "d", "a"]
p.isSubsetOf(q)        // p是否是q的子集
p.isStrictSubsetOf(q)  // p是否是q的真子集

// 8.超集和真超集
// isSupersetOf:超集(A是B的超集,表示A中包含B的所有元素,其中包括A==B)
// isStrictSupersetOf:真超集(A是B的真超集,表示A中包含B的所有元素,但是A!=B)
q.isSupersetOf(p)         // q是否是p的超集
q.isStrictSupersetOf(p)   // q是否是p的真超集

// 9.判断两个集合是否相离(完全没有公共元素)
p.isDisjointWith(q)

5.正确选择数据类型

数组:有序排列

集合:无序、唯一性、提供集合操作、快速查找

字典:键值数据对,无序

6.再看for-in

// 1.对Range使用for-in
for number in 1..<10 {
    print(number)
}

// 2.对String.characters使用for-in
for c in "Hello".characters {
    print(c)
}

// 3.对Array使用for-in
var vowels = ["a", "e", "i", "o", "u"]
for vowel in vowels {
    print(vowel)
}
for (i, vowel) in vowels.enumerate() {
    print("\(i): \(vowel)")
}

// 4.对字典使用for-in
var dict = [1: "A", 2: "B", 3: "C"]
for key in dict.keys {
    print(key)
}
for value in dict.values {
    print(value)
}
for (key, value) in dict {
    print("\(key): \(value)")
}

// 5.对集合使用for-in
var set1 = Set(["2", "4", "F"])
for s in set1 {
    print(s)
}

 

八、函数

1.函数的声明与调用

1)无参数无返回值的函数

// func:函数关键字
// printHello1:函数名
func printHello1() {
    print("Hello")
}

// func:函数关键字
// printHello2:函数名
func printHello2() -> Void {
    print("Hello")
}

2)有参数有返回值的函数

// func:函数关键字
// sayHello1:函数名
// name: String:参数及参数类型
// -> String:返回值为String类型
func sayHello1(name: String) -> String {
    return "Hello" + name
}

3)参数类型为可选型的函数

// func:函数关键字
// sayHello2:函数名
// name: String?:参数及参数类型(参数类型为可选型)
// -> String:返回值为String类型
func sayHello2(name: String?) -> String {
    // name ?? "Gest" (如果可选型name有值,name就是name,否则name就是字符串"Gest")
    return "Hello" + (name ?? "Gest")
}

4)返回值为元祖类型或元祖可选型类型

// 1)返回的元祖中不指明元祖元素名
func findMaxAndMin1(numbers: [Int]) -> (Int, Int) {
    var minValue = numbers[0]
    var maxValue = numbers[0]
    for number in numbers {
        minValue = minValue < number ? minValue : number
        maxValue = maxValue > number ? maxValue : number
    }
    return (maxValue, minValue)
}

// 2)返回的元祖中指明元祖元素名
func findMaxAndMin2(numbers: [Int]) -> (max: Int, min: Int) {
    var minValue = numbers[0]
    var maxValue = numbers[0]
    for number in numbers {
        minValue = minValue < number ? minValue : number
        maxValue = maxValue > number ? maxValue : number
    }
    // 注意:在这里由于已经指明了元组的元素名,所以这里的返回元祖可以不用指明元素名,如果要指明元素名,则必须使用原始的指定名;否则会报错
    return (maxValue, minValue)
}

// 3)由于存在参数numbers可能为空的情况,所以我们返回还是最好是元祖类型的可选型
func findMaxAndMin3(numbers: [Int]) -> (max: Int, min: Int)? {
    // 方法一:如果参数数组为空的话,直接返回nil
    if numbers.isEmpty {
        return nil
    }
    
    // 方法二:guard关键字保卫参数数组的长度不为0的条件
    // guard numbers.count > 0 else {
    //     return nil
    // }
    
    var minValue = numbers[0]
    var maxValue = numbers[0]
    for number in numbers {
        minValue = minValue < number ? minValue : number
        maxValue = maxValue > number ? maxValue : number
    }
    
    // 注意:在这里由于已经指明了元组的元素名,所以这里的返回元祖可以不用指明元素名,如果要指明元素名,则必须使用原始的指定名;否则会报错
    return (maxValue, minValue)
}
// 对于第三那种方法,函数调用如下: var scores1: [Int] = [202, 1234, 5678, 223, 34, 12] // 返回的是可选型,所以需要解包 if let result = findMaxAndMin3(scores1) { print("最大值为:\(result.max)") print("最小值为:\(result.min)") } // 4)由于存在网络的问题,可能我们获取这个参数数组的时候不成功(为nil),所以我们在函数调用的时候最好做一下判断 // 对于第三那种方法,函数使用如下: // 这里为更好地接收可能为nil的情况,所以必须设置变量类型为可选型 var scores2: [Int]? = [202, 1234, 5678, 223, 34, 12] // 如果scores2获取不成功就设置为空的数组 scores2 = scores2 ?? [] // 返回的是可选型,所以需要解包 // scores2作为参数传递,必须进行解包,我们可以进行强制解包,因为这个时候我们能够保证scores2不再为nil if let result = findMaxAndMin3(scores2!) { print("最大值为:\(result.max)") print("最小值为:\(result.min)") }

5)多个参数的函数

a.不区分内部函数名和外部函数名

// greeting:既是内部函数名,也是外部参数名
func sayHelloTo1(name: String, greeting: String) -> String {
    return "\(greeting) \(name)"
}

// 函数的调用
sayHelloTo1("Frank", greeting: "招呼语:")

b.区分内部函数名和外部函数名

// 内部参数名与外部参数名分开
// withGreetingWord:外部参数名
// greeting:内部参数名
func sayHelloTo2(name: String, withGreetingWord greeting: String) -> String {
    return "\(greeting) \(name)"
}

// 函数的调用
sayHelloTo2("Frank Li", withGreetingWord: "招呼语:")

c.忽略外部参数名

// 忽略外部参数名
func mutiply(num1: Int, _ num2: Int) -> Int {
    return num1 * num2
}

// 函数的调用
mutiply(2, 7)

d.有默认参数值的函数

// 最好将有默认参数值的参数放在最后面,这样少很多不必要的麻烦
func sayHelloTo(name: String, withGreetingWord greeting: String = "Hello", punctuation: String = "!") -> String {
    return "\(greeting), \(name)\(punctuation)"
}

// 函数的调用
// 由于有三个参数,两个有默认值,所以调用也就存在多种
// 甚至还可以将后面的两个有参数默认值的参数顺序对调
sayHelloTo("Frank")
sayHelloTo("Frank", withGreetingWord: "Hi")
sayHelloTo("Frank", withGreetingWord: "Hello", punctuation: "!!!")
sayHelloTo("Frank", punctuation: "!!!", withGreetingWord: "Hello")


// 另外,我们的打印函数print函数就是有默认参数值的函数
// separator:默认为空格,terminator:默认为回车换行
print("Frank", 1, 2, 3) // Frank 1 2 3\n
print("Frank", 1, 2, 3, separator: "@", terminator: "/") // Frank@1@2@3/

e.变长参数的函数

// 变长参数(不知道外界传过来多少个Double类型的值)
func mean(numbers: Double ...) -> Double {
    var sum: Double = 0
    
    // 将变长参数当做一个数组看待
    for number in numbers {
        sum += number
    }
    return sum / Double(numbers.count)
}

// 函数调用
mean(1)
mean(2, 1)
mean(2, 3, 4)
// ...
// 注意:对于一个函数只允许一个参数为变长类型

2.函数参数的按值传入和按引用传入

1)按值传入:无法改变函数在调用时,传入变量的值

a.只是读取参数的值,而不需要改变参数的值

 不用去给函数中参数声明是let,还是var,默认就是let

func findMaxAndMin(numbers: [Int]) -> (max: Int, min: Int)? {
    guard numbers.count != 0 else {
        return nil
    }
    
    var minValue = numbers[0]
    var maxValue = numbers[0]
    for number in numbers {
        minValue = min(minValue, number)
        maxValue = max(maxValue, number)
    }
    return (maxValue, minValue)
}

b.改变参数的值

// 注意:不会改变外部调用函数时传入参数的值,是因为函数里面重新拷贝了一份改传入的变量

// 1)实现将十进制Int类型数转化为二进制字符串
func toBinary(num: Int) -> String {
    var num = num
    var res = ""
    repeat {
        res = String(num % 2) + res
        num /= 2
    } while num != 0
    return res
}

// 函数调用
let num: Int = 4
toBinary(num)


// 2)实现两个值的交换
func swapTwoInts(var a: Int, var _ b: Int) {
    let t: Int = a
    a = b
    b = t
}

// 函数调用
var x: Int = 1
var y: Int = 2
swapTwoInts(x, y)

2)按引用传入:同时改变函数在调用时,传入变量的值

// 注意:inout关键字就是能够保证参数的引用传入,外部调用函数时的变量的值也会跟着发生改变

// 1)实现两个值的交换
func swapTwoInt(inout a: Int, inout _ b: Int) {
    (a, b) = (b, a)
}

// 函数调用
var p: Int = 1
var q: Int = 2
swapTwoInt(&p, &q)


// 2)数组的按引用传入
func initArray(inout arr: [Int], by value: Int) {
    for i in 0..<arr.count {
        arr[i] = value
    }
}

// 函数调用
var arr = [1, 2, 3, 4, 5, 6]
initArray(&arr, by: 2)

3. 函数类型的变量

1)将函数作为变量 -> 函数类型的变量

// 1.代码示例一
func add(a: Int, _ b: Int) -> Int {
    return a + b
}

// 函数调用
// 变量anotherAdd:函数类型 (Int, Int) -> Int
let anotherAdd: (Int, Int) -> Int = add
anotherAdd(3, 4)


// 2)代码示例二
func sayHelloTo(name: String) {
    print("Hello, \(name)!")
}

// 函数调用
let anotherSayHelloTo: (String) -> Void = sayHelloTo
anotherSayHelloTo("Frank")

2)sort函数 -> 数组排序的函数,其传入参数就是一个函数类型的变量

var arr: [Int] = [10, 20, 231, 111, 128, 60, 8]

// 1.arr数组从小到大排序
// a.sort不会改变原来的数组,而是重新生成一个新的数组
arr.sort()
// b.sortInPlace会改变原来的数组
arr.sortInPlace()

// 2.arr数组从大到小排序
func biggerNumberFirst(a: Int, _ b: Int) -> Bool {
    return a > b
}
// a.sort不会改变原来的数组,而是重新生成一个新的数组
arr.sort(biggerNumberFirst)
// b.sortInPlace会改变原来的数组
arr.sortInPlace(biggerNumberFirst)

// 3.arr数组按照字符串从小到大排序
func cmpByNumberString(a: Int, _ b: Int) -> Bool {
    return String(a) < String(b)
}
// a.sort不会改变原来的数组,而是重新生成一个新的数组
arr.sort(cmpByNumberString)
// b.sortInPlace会改变原来的数组
arr.sortInPlace(cmpByNumberString)

// 4.数组按照距离某一个值的远近来从近到远排序
func near50(a: Int, _ b: Int) -> Bool {
    // return abs(a - 50) < abs(b - 50) ? true : false
    return abs(a - 50) < abs(b - 50)
}
// a.sort不会改变原来的数组,而是重新生成一个新的数组
arr.sort(near50)
// b.sortInPlace会改变原来的数组
arr.sortInPlace(near50)

注意:sort函数是专门针对数组排序的,排序后会重新生成一个新的数组,不会影响改变原来的数组;而与它相对的另一个排序函数sortInPlace会改变原来的数组

4.高阶函数

1)高阶函数的认识

a.一阶函数

// 某一个班在某次学科考试上的分数数组
var scores = [120, 96, 100, 145, 114]

// 规则一:比如讲,某一个老师因为某一题目出错了,想要给所有学生加上那道题的分数
func changScores1(inout scores: [Int]) {
    for (index, score) in scores.enumerate() {
        scores[index] = score + 5
    }
}

// 规则二:比如讲,这次考试是在150满分下的学生得分,希望能够拿到100满分下的得分
func changScores2(inout scores: [Int]) {
    for (index, score) in scores.enumerate() {
        scores[index] = Int(Double(score) / 150.0 * 100.0)
    }
}

changScores1(&scores)
changScores2(&scores)

b.高阶函数

 从上面可以看出来,如果每次考试的规则都不一样,那么我们每次在考完一次试后都要去根据不同的规则而去反复修改我们的主题函数,那么这就有了高阶函数

// 某一个班在某次学科考试上的分数数组
var scores = [120, 96, 100, 145, 114]

// 高阶函数的主体
func changeScores(inout scores: [Int], by changeScore: (Int) -> Int) {
    for (index, score) in scores.enumerate() {
        scores[index] = changeScore(score)
    }
}

// 函数调用
// 1)某一次考试的谋一道题目出错啦,老师想给每位同学加上这道题的分数
func changScores1(score: Int) -> Int {
    return score + 5
}
changeScores(&scores, by: changScores1)

// 2)这次考试是在150满分下的学生得分,希望能够拿到100满分下的得分
func changScores2(score: Int) -> Int {
    return Int(Double(score) / 150.0 * 100.0)
}
changeScores(&scores, by: changScores2)

 这样每次考试,如果规则不同,我们只需去编写每次的考试规则就OK啦,不必每次去修改主体函数

2)最基本的三个高阶函数 --- map、filter、reduce

a.map操作 -- 原来数组发生改变

 含义:将数组中的每一项元素进行某一函数操作后返回的值再然后再存进数组

func changScores(score: Int) -> Int {
    return score + 10
}

var scores = [36, 65, 78, 90]
// 对数组scores调用map函数
scores.map(changScores)   // scores = [46, 75, 88, 100]

b.Filter操作

 含义:将数组中的每一项元素进行某一函数操作,返回满足条件的元素数组

func fail(score: Int) -> Bool {
    return score < 60
}

var scores = [36, 65, 78, 90, 50]
// 对数组scores调用filter函数
scores.filter(fail)   // [36, 50]

c.reduce操作

 含义:将数组中的每一项元素进行某一函数操作,返回函数操作后的结果

// 1.实例一
func add(num1: Int, _ num2: Int) -> Int {
    return num1 + num2
}

var scores1 = [1, 2, 3, 4]
// 对数组scores1调用reduce函数
scores1.reduce(0, combine: add)   // 10
// 效果同上:加号(+)就是求和函数的意思
scores1.reduce(0, combine: +)
// 注意:其中第一个参数也会被累计进去,其实就本例而言,相当于:0+1+2+3+4 = 10


// 2.实例二
func concatenate(str: String, num: Int) -> String {
    return str + String(num) + ""
}

var scores2 = [1, 2, 3, 4]
// 对数组scores2调用reduce函数
scores2.reduce("", combine: concatenate)   // “1234”

5.返回函数类型和函数嵌套

1)返回函数类型

// 邮费计算方式一
func tier1MailFeeByWeight(weight: Int) -> Int {
    return 1 * weight
}

// 邮费计算方式二
func tier2MailFeeByWeight(weight: Int) -> Int {
    return 2 * weight
}

// 邮费计算函数 -- 返回函数类型
func chooseMailFeeCalculationByWeight(weight: Int) -> (Int) -> Int {
    return weight <= 10 ? tier1MailFeeByWeight : tier2MailFeeByWeight
}

// 商品总价格计算函数
func feeByUnitPrice(price: Int, weight: Int) -> Int {
    let mailFeeByWeight = chooseMailFeeCalculationByWeight(weight)
    let totalPrice = price * weight
    
    return mailFeeByWeight(weight) + totalPrice;
}

2)函数嵌套

// 邮费计算方式一
func tier1MailFeeByWeight(weight: Int) -> Int {
    return 1 * weight
}

// 邮费计算方式二
func tier2MailFeeByWeight(weight: Int) -> Int {
    return 2 * weight
}

// 商品总价格计算函数 -- 函数嵌套(嵌套chooseMailFeeCalculationByWeight)
func feeByUnitPrice(price: Int, weight: Int) -> Int {
    
    // 邮费计算函数 -- 返回函数类型
    func chooseMailFeeCalculationByWeight(weight: Int) -> (Int) -> Int {
        return weight <= 10 ? tier1MailFeeByWeight : tier2MailFeeByWeight
    }
    
    let mailFeeByWeight = chooseMailFeeCalculationByWeight(weight)
    let totalPrice = price * weight
    
    return mailFeeByWeight(weight) + totalPrice;
}

 

九、闭包

1.闭包的基本语法

 闭包与OC中的Block类似,{}中的内容就是闭包的内容

var arr = [12, 21, 1, 2, 333, 10]

// 1.基本函数
func biggerNumberFirst(a: Int, _ b: Int) -> Bool {
    return a > b
}

// 调用sort函数对数组arr排序
arr.sort(biggerNumberFirst)


// 2.使用闭包
// 调用sort函数对数组arr排序
arr.sort({(a: Int, b: Int) -> Bool in
    // 闭包的函数体
    return a > b
})

2.闭包的简化

var arr = [12, 21, 1, 2, 333, 10]

// 闭包的简化
arr.sort({(a: Int, b: Int) -> Bool in
    // 闭包的函数体
    return a > b
})

arr.sort({(a: Int, b: Int) -> Bool in return a > b})

arr.sort({a, b in return a > b})

arr.sort({a, b in a > b})

arr.sort({$0 > $1})

arr.sort(>)

3.结尾闭包

// 1.结尾闭包
var arr = [10, 23, 21, 12, 34, 32, 22, 54, 33]

// 对于结尾只有一个参数的闭包,可以将{}写在外面
arr.sort(){a, b in
    return a > b
}

// 那么如果除闭包外,再无其他参数传入,我们也可以省略()
arr.sort{a, b in
    return a > b
}


// 2.UIView动画的实例
let showView = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
showView.backgroundColor = UIColor.blackColor()
XCPlaygroundPage.currentPage.liveView = showView

let rectangleView = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
rectangleView.center = showView.center
rectangleView.backgroundColor = UIColor.redColor()
showView.addSubview(rectangleView)

// 写法一
UIView.animateWithDuration(2.0, animations: {
    rectangleView.backgroundColor = UIColor.blueColor()
    rectangleView.frame = showView.frame
})

// 写法二
UIView.animateWithDuration(2.0){
    rectangleView.backgroundColor = UIColor.blueColor()
    rectangleView.frame = showView.frame
}

4.内容捕获 -- 捕获闭包外面的值

var arr: [Int] = []
for _ in 0 ..< 100 {
    arr.append(random()%100)
}

// 1.定值300
arr.sort{a, b in
    abs(a - 300) < abs(b - 300)
}

// 2.捕获值num
var num = 700
arr.sort{a, b in
    abs(a - num) < abs(b - num)
}

5.闭包和函数是引用类型

// runningMetersWithMetersPerFDay函数
// 参数是Int类型
// 返回值为函数类型:参数为空,返回Int类型
func runningMetersWithMetersPerFDay(metersPerDay: Int) -> () -> Int {
    var totalMeters = 0
    return {
        totalMeters += metersPerDay
        return totalMeters
    }
}

var planA = runningMetersWithMetersPerFDay(2000)
planA() // 2000
planA() // 4000

var planB = runningMetersWithMetersPerFDay(5000)
planB() // 5000
planB() // 10000

var anotherPlan = planB
anotherPlan() // 15000
planB()       // 20000


let planC = runningMetersWithMetersPerFDay(3000)
planC() // 3000
planC() // 6000
// 报错:因为改变了常量planC的值
// planC = runningMetersWithMetersPerFDay(6000)

 

十、枚举

1.枚举的定义

// 1.写法一
enum Month1{
    case January
    case February
    case March
    case April
    case May
    case June
    case July
    case August
    case September
    case October
    case November
    case December
}

// 2.写法二
enum Month2{
    case January, February, March, April, May, June, July, August, September, October, November, December
}

2.枚举变量的使用

enum Month{
    case January
    case February
    case March
    case April
    case May
    case June
    case July
    case August
    case September
    case October
    case November
    case December
}

// 1.隐式
// currentMonth虽然没有指明变量类型,但是系统自己判断为Month类型
let currentMonth = Month.July

// 2.显示
// lastMonth已经显式声明了变量类型,所以在赋值的时候可以省略Month
let lastMonth: Month = .August

// 3.枚举类型的变量在swicth语句中使用
// 1)写法一
func season1(month: Month) -> String {
    switch month {
    case Month.March, Month.April, Month.May:
        return "Spring"
    case Month.June, Month.July, Month.August:
        return "Summer"
    case Month.September, Month.October, Month.November:
        return "Autumn"
    case Month.December, Month.January, Month.February:
        return "Winter"
    }
}
// 2)写法二
func season2(month: Month) -> String {
    switch month {
    case .March, .April, .May:
        return "Spring"
    case .June, .July, .August:
        return "Summer"
    case .September, .October, .November:
        return "Autumn"
    case .December, .January, .February:
        return "Winter"
    }
}

3.枚举的原始值(Raw Value)

1)定义原始值

// 1.写法一
enum Month1: Int{
    case January = 1
    case February = 2
    case March = 3
    case April = 4
    case May = 5
    case June = 6
    case July = 7
    case August = 8
    case September = 9
    case October = 10
    case November = 11
    case December = 12
}

// 2.写法二
enum Month2: Int{
    case January = 1, February = 2, March = 3, April = 4, May = 5, June = 6, July = 7, August = 8, September = 9, October = 10, November = 11, December = 12
}

// 3.写法三(对于Int类型的原始值,我们只需要指定第一个,后面的依次+1)
enum Month3: Int{
    case January = 1, February, March, April, May, June, July, August, September, October, November, December
}

2)原始值的使用

enum Month: Int{
    case January = 1
    case February = 2
    case March = 3
    case April = 4
    case May = 5
    case June = 6
    case July = 7
    case August = 8
    case September = 9
    case October = 10
    case November = 11
    case December = 12
}

func monthsBeforeNewYear(month: Month) -> Int {
    // month.rawValue 获取month的原始值
    return 12 - month.rawValue
}

let month1 = Month.April
monthsBeforeNewYear(month1)

let input = 4
// 返回值为可选型
let month2 = Month(rawValue: input)
// 可选型使用时必须进行解包
if let month2 = month2 {
    monthsBeforeNewYear(month2)
}

3)默认原始值

a.Int类型的原始值 -- 默认第一个为0

// 枚举的原始值第一个默认为0
enum Grade: Int {
    case F, E, D, C, B, A
}

let grade: Grade = .A
print("Your score is \(grade.rawValue)")

b.String类型的原始值 -- 默认就是其各自的本身

// 1.写法一
enum ProgrammingLanguage1: String {
    case Swift = "Swift"
    case ObjectiveC = "Objective_C"
    case C = "C"
    case Java = "Java"
}

// 2.写法二(字符创类型默认为它本身)
enum ProgrammingLanguage2: String {
    case Swift
    case ObjectiveC = "Objective_C" // 对于这个我们需要指明,是因为变量名不可以出现下划线
    case C
    case Java
}

let myFavoriteLanguage1: ProgrammingLanguage1 = .Swift
print("\(myFavoriteLanguage1.rawValue) is my favorite language.")

let myFavoriteLanguage2: ProgrammingLanguage2 = .Swift
print("\(myFavoriteLanguage2.rawValue) is my favorite language.")

4.枚举的关联值(Associate Value)

 注意:枚举的关联值和原始值是相互矛盾的,不可以同时使用

1)关联一个值

// 定义一个枚举
enum ATMStatus{
    case Success(Int)  // 给这个枚举值关联一个Int类型的值(这个Int值是变化的)
    case Error(String) // 给这个枚举值一个String类型的值(这个String值是变化的)
    case Waiting       // 不给枚举值关联值
}

// 提款函数(参数:提款金额,返回值:返回提款信息(枚举值))
var balance = 1000 // 当前账户的金额
func withdraw(amount: Int) -> ATMStatus {
    if balance >= amount {
        balance -= amount
        // 这个枚举值关联一个Int类型的变量balance(账户余额)
        return .Success(balance)
    } else {
        // 这个枚举值关联一个String类型的变量(错误信息)
        return .Error("余额不足")
    }
}

// 提款1100元
let result = withdraw(1100)
switch result {
case let .Success(newBalance):
    print("账户余额:\(newBalance)元.")
case let .Error(errorMessage):
    print("提款失败:\(errorMessage)")
case .Waiting:
    print("稍等...")
}

// 运行结果:提款失败:余额不足

2)关联多个值

// 定义一个枚举
enum Shape{
    case Square(side: Double)
    case Rectangle(width: Double, height: Double)
    case Circle(centerX: Double, centerY: Double, radius: Double)
    case Point
}

let square = Shape.Square(side: 10)
let rectangle = Shape.Rectangle(width: 20, height: 30)
let circle = Shape.Circle(centerX: 0, centerY: 0, radius: 20)
let point = Shape.Point

// 计算图形面积的函数
func area(shape: Shape) -> Double {
    switch shape {
    case let .Square(side):
        return side * side
    case let .Rectangle(width, height):
        return width * height
    case let .Circle(_, _, r):   // 使用下划线忽略没用的参数
        return M_PI * r * r
    case .Point:
        return 0
    }
}

5.可选型 & 枚举

 注意:可选型其实质就是枚举类型

// Int类型的可选型:
var age1: Int? = 17
// 对应的枚举类型:
var age2: Optional<Int> = Optional.Some(17)


// String类型的可选型:
var name1: String? = "Frank"
// 对应的枚举类型:
var name2: Optional<String> = Optional.Some("Frank")

// 设置为nil:
name1 = nil
// 对应的枚举可选型的nil:
name2 = .None // 因为已经声明了name是Optional类型,所以只需要直接点就好

// 枚举的解包:
switch name2 {
case .None:
    print("无名英雄")
case let .Some(name):
    print("姓名:\(name)")
}
// 可选型的解包:
if let name1 = name1 {
    print("姓名:\(name1)")
} else {
    print("无名英雄");
}

6.枚举递归

// 定义枚举递归的两种方法:
// 1.写法一
indirect enum ArithmeticExpression1{
    case Number(Int)
    case Addition(ArithmeticExpression1, ArithmeticExpression1)
    case Multiplication(ArithmeticExpression1, ArithmeticExpression1)
}
// 2.写法二
enum ArithmeticExpression2{
    case Number(Int)
    indirect case Addition(ArithmeticExpression2, ArithmeticExpression2)
    indirect case Multiplication(ArithmeticExpression2, ArithmeticExpression2)
}

// 求值函数 -- 递归函数
func evaluate(expression: ArithmeticExpression1) -> Int {
    switch expression {
    case let .Number(Value):
        return Value
    case let .Addition(left, right):
        return evaluate(left) + evaluate(right)
    case let .Multiplication(left, right):
        return evaluate(left) * evaluate(right)
    }
}

// 函数调用
// 计算:(5 + 4) * 2
let five = ArithmeticExpression1.Number(5)
let four = ArithmeticExpression1.Number(4)
let sum = ArithmeticExpression1.Addition(five, four)
let two = ArithmeticExpression1.Number(2)
let product = ArithmeticExpression1.Multiplication(sum, two)
evaluate(product)

// 运行结果:18

 

十一、结构体

1.结构体基础

1)结构体声明

// 结构体的声明
struct Location{
    var latitude: Double    // 变量
    let longitude: Double   // 常量
}

// 定义一个Location结构体类型的常量
let location1 = Location(latitude: 10, longitude: 50)
// 定义一个Location结构体类型的变量
var location2: Location = Location(latitude: -30, longitude: -100)

// 访问结构体的值
location1.latitude
location2.latitude

// 对于常量location1来说,它的某一个值是不可以改变的,它的整体值也是不可以改变的
// location1 = location2   // 报错
// location1.latitude = 20                 // 报错
// location1.longitude = 40                // 报错

// 对于变量googleHeadOuarterLocation来说,是可以改变它的某一个同为变量的值,同是也可以改变其整体值
location2 = location1  // 注意:这个时候实际上是分别改变结构体里面的值
location2.latitude = 0
// location2.longitude = 0  // 报错

2)结构体嵌套

struct Location{
    var latitude: Double    // 变量
    let longitude: Double   // 常量
}

// 结构体嵌套
struct Place{
    let location: Location
    var name: String
}

var location = Location(latitude: 20, longitude: 10)
var place = Place(location: location, name: "HuBei")

// 访问
place.location.latitude

2.结构体的构造函数

1)结构体的默认构造函数

struct Location{
    let latitude: Double
    let longitude: Double
}
// 初始化 let location = Location(latitude: 20, longitude: 100)

2)结构体初值

struct Location{
    var latitude: Double = 0   // 给结构体赋初值,既然赋初值,必定是变量类型
    var longitude: Double = 0  // 给结构体赋初值,既然赋初值,必定是变量类型
}
// 初始化 let location1 = Location() let location2 = Location(latitude: 20, longitude: 100)

3)结构体自定义构造函数

struct Location{
    let latitude: Double
    let longitude: Double
    
    // 构造方法:"20,30"
    init(coordinateString: String) {
        let commaIndex = coordinateString.rangeOfString(",")!.startIndex
        let firstElement = coordinateString.substringToIndex(commaIndex)
        let secondElement = coordinateString.substringFromIndex(commaIndex.successor())
        
        // 注意:这里可以省略self,因为不存在结构体属性名混淆
        self.latitude = Double(firstElement)!
        self.longitude = Double(secondElement)!
    }
    
    // 注意:这里必须要使用self,代表自己;因为传参与结构体属性名混淆
    init(latitude: Double, longitude: Double) {
        self.latitude = latitude
        self.longitude = longitude
    }
}
// 初始化 let location1 = Location(coordinateString: "20,30") let location2 = Location(latitude: 30, longitude: 40)

4)结构体没有进行赋初值的,就是没有初值,这样就是不能够被使用;但是可选型是个例外

struct Location{
    let latitude: Double
    let longitude: Double
    var placeName: String?  // 可选型,没有赋初值,但是默认为nil
    
    // 构造方法:"20,30"
    init(coordinateString: String) {
        let commaIndex = coordinateString.rangeOfString(",")!.startIndex
        let firstElement = coordinateString.substringToIndex(commaIndex)
        let secondElement = coordinateString.substringFromIndex(commaIndex.successor())
        
        // 注意:这里可以省略self,因为不存在结构体属性名混淆
        self.latitude = Double(firstElement)!
        self.longitude = Double(secondElement)!
    }
    
    // 注意:这里必须要使用self,代表自己;因为传参与结构体属性名混淆
    init(latitude: Double, longitude: Double) {
        self.latitude = latitude
        self.longitude = longitude
    }
    
    // 注意:这里必须要使用self,代表自己;因为传参与结构体属性名混淆
    init(latitude: Double, longitude: Double, placeName: String) {
        self.latitude = latitude;
        self.longitude = longitude;
        self.placeName = placeName;
    }
}
// 初始化 let location1 = Location(coordinateString: "20,30") let location2 = Location(latitude: 30, longitude: 40) let location3 = Location(latitude: 20, longitude: 10, placeName: "Frank")

3.可失败的构造函数

// 1.写法一
struct Location1{
    let latitude: Double
    let longitude: Double
    var placeName: String?  // 可选型,没有赋初值,但是默认为nil
    
    // 这里传过来的参数字符串有可能不是下面这个格式("20,30")的,所以这个时候我们应该返回一个nil
    // 使用Failable-Initializer
    init?(coordinateString: String) {
        // 使用if-else反复尝试解包
        if let commaIndex = coordinateString.rangeOfString(",")?.startIndex {
            if let firstElement = Double(coordinateString.substringToIndex(commaIndex)) {
                if let secondElement = Double(coordinateString.substringFromIndex(commaIndex.successor())) {
                    // 注意:这里可以省略self,因为不存在结构体属性名混淆
                    self.latitude = firstElement
                    self.longitude = secondElement
                }
                else {
                    return nil
                }
            }
            else {
                return nil
            }
        }
        else {
            return nil
        }
    }
}

// 2.写法二
struct Location2{
    let latitude: Double
    let longitude: Double
    var placeName: String?  // 可选型,没有赋初值,但是默认为nil
    
    // 这里传过来的参数字符串有可能不是下面这个格式("20,30")的,所以这个时候我们应该返回一个nil
    // 使用Failable-Initializer
    init?(coordinateString: String) {
        // 使用三个guard关键字来保卫三个条件
        guard let commaIndex = coordinateString.rangeOfString(",")?.startIndex else {
            return nil
        }
        guard let firstElement = Double(coordinateString.substringToIndex(commaIndex)) else {
            return nil
        }
        guard let secondElement = Double(coordinateString.substringFromIndex(commaIndex.successor())) else {
            return nil
        }
        // 注意:这里可以省略self,因为不存在结构体属性名混淆
        self.latitude = firstElement
        self.longitude = secondElement
    }
}

// 3.写法三
struct Location3{
    let latitude: Double
    let longitude: Double
    var placeName: String?  // 可选型,没有赋初值,但是默认为nil
    
    // 这里传过来的参数字符串有可能不是下面这个格式("20,30")的,所以这个时候我们应该返回一个nil
    // 使用Failable-Initializer
    init?(coordinateString: String) {
        // 使用一个guard关键字来保卫三个条件,条件之间用逗号隔开
        guard
            let commaIndex = coordinateString.rangeOfString(",")?.startIndex,
            let firstElement = Double(coordinateString.substringToIndex(commaIndex)),
            let secondElement = Double(coordinateString.substringFromIndex(commaIndex.successor()))
        else {
            return nil
        }
        // 注意:这里可以省略self,因为不存在结构体属性名混淆
        self.latitude = firstElement
        self.longitude = secondElement
    }
}

4.在结构体和枚举中定义方法

1)在结构体中定义方法

struct Location{
    let latitude: Double
    let longitude: Double
    
    // 方法
    func printLocation() {
        print("The Location is \(self.latitude), \(self.longitude)")
    }
    func isNorth() -> Bool {
        return self.latitude > 0
    }
    func isSouth() -> Bool {
        return !self.isNorth()
    }
    func distanceTo(location: Location) -> Double {
        // sqrt(x) 对x开平方根
        // pow(x, y) 求x的y次方幂
        return sqrt(pow(self.latitude - location.latitude, 2) + pow(self.longitude - location.longitude, 2))
    }
}

// 函数调用
let location1 = Location(latitude: 30, longitude: 100)
let location2: Location = Location(latitude: 40, longitude: 180)
location1.printLocation()
location1.isNorth()
location1.isSouth()
location1.distanceTo(location2)

2)在枚举中定义方法

enum Shape{
    case Square(side: Double)
    case Rectangle(width: Double, height: Double)
    case Circle(centerX: Double, centerY: Double, radius: Double)
    case Point
    
    // 方法
    func area() -> Double {
        switch self {
        case let .Square(side):
            return side * side
        case let .Rectangle(width, height):
            return width * height
        case let .Circle(_, _, r):
            return M_PI * r * r
        case .Point:
            return 0
        }
    }
}

// 函数调用
let square = Shape.Square(side: 10)
let rectangle = Shape.Rectangle(width: 20, height: 30)
let circle = Shape.Circle(centerX: 0, centerY: 0, radius: 15)
let point = Shape.Point
square.area()
rectangle.area()
circle.area()
point.area()

5.总结:

1)结构体和枚举都是值类型

a.结构体是值类型

struct Point{
    var x = 0
    var y = 0
}

var p1 = Point()
// 这个时候是完全将p1拷贝一份,p1和p2是完全不同的两个结构体变量
var p2 = p1

p2.x += 1
p2         // x = 1, y = 0
p1         // x = 0, y = 0

b.枚举是值类型

// 1)
enum Direction{
    case North
    case South
    case East
    case West
}

var d1 = Direction.North
// 这个时候是完全将d1拷贝一份,d1和d2是完全不同的两个枚举变量
var d2 = d1

d2 = Direction.South

d2   // d2 = South
d1   // d2 = North


// 2)
enum Shape{
    case Square(side: Double)
    case Rectangle(width: Double, height: Double)
    case Circle(cencerX: Double, centerY: Double, radius: Double)
}

var square1 = Shape.Square(side: 2.0)
// 这个时候是完全将square1拷贝一份,square1和square2是完全不同的两个枚举变量
var square2 = square1

square2 = Shape.Square(side: 5.0)
square2   // 5
square1   // 2

2)结构体在Swift中无处不在

a.Array、Dictionary、Set都是结构体

b.Int、Float、Double、Bool、String都是结构体

var a: Int = 1
a.distanceTo(100)
// 其实这个Int类型就是个结构体类型,方法distanceTo()就是定义在Int结构体内的方法

 

十二、类

1.类的基础

1)类的声明

// 类不同于结构体的是:类没有默认的init方法,如果既没给类中的成员变量赋初值,也没手动定义类的初始化方法,程序就会报错

// 类的声明
class Person {
    var firstName: String
    var lastName: String
    
    // 定义初始化方法
    init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
    }
    
    // 定义初始化方法
    init?(fullName: String) {
        guard
            let spaceIndex = fullName.rangeOfString(" ")?.startIndex
            else {
                return nil
        }
        
        self.firstName = fullName.substringToIndex(spaceIndex)
        self.lastName = fullName.substringFromIndex(spaceIndex.successor())
    }
    
    // 类的方法
    func fullName() -> String {
        return firstName + " " + lastName
    }
}

// 类对象的初始化
let person1 = Person(firstName: "Frank", lastName: "Li")
let person2 = Person(fullName: "Frank Li")
let person3 = Person(fullName: "")

// 类中方法的调用
person1.fullName()
person2?.fullName()
person3?.fullName()

2)类是引用类型,这个也是它不同于结构体的地方

class Person {
    var firstName: String
    var lastName: String
    var career: String?
    
    // 定义初始化方法
    init(firstName: String, lastName: String, career: String) {
        self.firstName = firstName
        self.lastName = lastName
        self.career = career
    }
}

let person1 = Person(firstName: "Frank", lastName: "Li", career: "Developer")
let person2 = person1
person2.firstName = "Dong"
person2.lastName = "Jin"
person2.career = "CEO"

person2
// firstName: "Dong"
// lastName: "Jin"
// career: "CEO"

person1
// firstName: "Dong"
// lastName: "Jin"
// career: "CEO"

 延伸知识:引用类型值类型的区别

a.引用类型

b.值类型

c.mutating关键字

// 1.结构体中的mutating
struct Location {
    var x = 0
    var y = 0
    
    // 自己修改自己:mutating引用类型
    mutating func goEast() {
        self.x += 1
    }
}

// 调用
var location = Location() // location.x = 0 location.y = 0
location.goEast() // location.x = 1 location.y = 0


// 2.枚举中的mutating
enum Switch {
    case On
    case Off
    
    mutating func click() {
        switch self {
        case .On:
            self = .Off
        case .Off:
            self = .On
        }
    }
}

// 调用
var button = Switch.Off  // button = Off
button.click() // button = On

2.类的等价

 ==:值的比较

 ===:引用类型的比较

class Person {
    var firstName: String
    var lastName: String
    var career: String?
    
    init(firstName: String, lastName: String, career: String) {
        self.firstName = firstName
        self.lastName = lastName
        self.career = career
    }
    
    init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
    }
    
    func fullName() -> String {
        return firstName + " " + lastName
    }
}

// 初始化对象
let person1 = Person(firstName: "Frank", lastName: "Li", career: "CEO")
let person2 = person1
let person3 = Person(firstName: "Frank", lastName: "Li", career: "CEO")

// 报错:==只用于判断值类型的相等
// person1 == person2

// ===:判断引用类型的相等
person1 === person2    // ture

// 虽然person1和person3的存放的值都是一样的,但是他们是两个不同的内存空间
person1 === person3    // false

// !==(不等于)
person1 !== person3     // ture

 

十三、属性和方法

1.存储属性和计算属性

1)概念

 存储型属性:单纯的存值

 计算型属性:依赖的属性

2)实例

struct Point {
    var x = 0.0
    var y = 0.0
}

struct Size {
    var width = 0.0
    var height = 0.0
}

class Rectangle {
    // 存储型属性
    var origin = Point()
    var size = Size()
    
    // 计算型属性(注意:计算型属性的值是随时可变的,所以必须声明为var)
    var center: Point {
        // 1.getter方法
        get{
            let centerX = origin.x + size.width / 2.0
            let centerY = origin.y + size.height / 2.0
            return Point(x: centerX, y: centerY)
        }
        // 2.setter方法
        set{
            // 重新计算origin.x和origin.y(假设width、heigth不变)
            origin.x = newValue.x - size.width / 2.0
            origin.y = newValue.y - size.height / 2.0
        }
    }
    
    // 计算型属性
    var area: Double {
        // 仅仅只有getter方法(也可以套上一个get)
        return size.width * size.height
    }

    // 初始化方法
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }
}

// 调用
var rectangle: Rectangle = Rectangle(origin: Point(x: 10, y: 10), size: Size(width: 20, height: 20))
// 1.调用getter方法
rectangle.center
// 2.调用setter方法
rectangle.center = Point(x: 10, y: 10)

2.类型属性和类型方法

1)类型属性

class Player {
    var name: String
    var score = 0
    // 类属性
    static var highestScore = 0  // 所有玩家中的最高分
    
    // 初始化方法
    init(name: String) {
        self.name = name
    }
    
    func play() {
        let score = random() % 100
        print("\(name) played and got \(score) scores.")
        
        self.score += score
        print("Total score of \(name) is \(self.score).")
        
        // Player.highestScore调用类属性
        if self.score > Player.highestScore {
            Player.highestScore = self.score
        }
        print("Highest score is \(Player.highestScore).")
    }
}

let player1 = Player(name: "Frank")
let player2 = Player(name: "Li dong")
player1.play()
player1.play()
player2.play()
player2.play()

 运行结果:

  // Frank played and got 83 scores.

  // Total score of Frank is 83.

  // Highest score is 83.

  // Frank played and got 86 scores.

  // Total score of Frank is 169.

  // Highest score is 169.

  // Li dong played and got 77 scores.

  // Total score of Li dong is 77.

  // Highest score is 169.

  // Li dong played and got 15 scores.

  // Total score of Li dong is 92.

  // Highest score is 169.

2)类型方法

struct Matrix {
    var m: [[Int]]  // 二维数组
    var row: Int    //
    var col: Int    //// 初始化方法(可选型)
    init?(_ arr2d: [[Int]]) {
        guard arr2d.count > 0 else {
            return nil
        }
        
        let row = arr2d.count
        let col = arr2d[0].count
        for i in 1..<row  {
            if arr2d[i].count != row {
                return nil
            }
        }
        
        self.m = arr2d
        self.row = row
        self.col = col
    }
    
    // 打印方法
    func printMatrix() {
        for i in 0..<row {
            for j in 0..<col {
                print(m[i][j], terminator: "\t")
            }
            print()
        }
    }
    
    // 类方法
    // 返回单位矩阵(Matrix类型的可选型)
    static func identityMatrix(n: Int) -> Matrix? {
        guard n > 0 else {
            return nil
        }
        
        // 创建一个空的2维数组
        var arr2d: [[Int]] = []
        for i in 0..<n {
            var rowArr = [Int](count: n, repeatedValue: 0)
            rowArr[i] = 1
            arr2d .append(rowArr)
        }
        // 因为Matrix的init方法为一个可选型,所以必须进行解包,或者同样用一个可选型接收
        // 1.可选型去接收
        // let matrix: Matrix? = Matrix(arr2d)
        // 2.强制解包(这里采用强制解包是安全的,因为init方法返回结果一定是有值的)
        let matrix: Matrix = Matrix(arr2d)!
        return matrix
    }
}

// 方法调用
// 由于是可选型,所以必须进行解包
if let m = Matrix([[1, 2], [3, 4]]) {
    m.printMatrix()
}

// 调用类方法
if let m = Matrix.identityMatrix(6) {
    m.printMatrix()
}

 运行结果:

  // 1 2

  // 3 4

  // 1 0 0 0 0 0

  // 0 1 0 0 0 0

  // 0 0 1 0 0 0

  // 0 0 0 1 0 0

  // 0 0 0 0 1 0

  // 0 0 0 0 0 1

3.属性观察器

1)实例

class LightBulb {
    // 最大限制电流
    static let maxCurrent = 30
    
    // 当前电流
    var current = 0 {
        // 属性观察器
        
        // 1.值将要发生改变
        willSet(newCurrent) {
            // 注意:这里也可以使用newValue,那么willSet中就不能传参
            print("Current value changed. The change is \(abs(current - newCurrent))")
        }
        
        // 2.值已经发生改变
        didSet(oldCurrent) {
            if current == LightBulb.maxCurrent {
                print("Pay attention, the current value get to the maximum point.")
            }
            else if current > LightBulb.maxCurrent {
                print("Current too high, falling back to previous setting.")
                // 注意:这里也可以使用oldValue,那么didSet中就不能传参
                current = oldCurrent
            }
            print("The current is \(current)")
        }
    }
}

// 调用
let bulb = LightBulb()
bulb.current = 20
bulb.current = 40

 运行结果:

  // Current value changed. The change is 20

  // The current is 20

  // Current value changed. The change is 20

   // Current too high, falling back to previous setting.

   // The current is 20

2)属性观察器的willSet和didSet方法在init初始化时和给属性赋初值的时候不会被调用

enum Theme {
    case DayMode
    case NightMode
}

class UI {
    var fontColor: UIColor!
    var backgroundColor: UIColor!
    
    // 属性观察器
    var themeMode: Theme = .DayMode {
        didSet{
            switch themeMode {
            case .DayMode:
                fontColor = UIColor.blackColor()
                backgroundColor = UIColor.whiteColor()
            case .NightMode:
                fontColor = UIColor.whiteColor()
                backgroundColor = UIColor.blackColor()
            }
        }
    }
    
    // 初始化方法
    init(themeMode: Theme) {
        self.themeMode = themeMode
    }
}

// 调用
let ui = UI(themeMode: .DayMode)
ui.themeMode         // DayMode
ui.fontColor         // 依然为nil
ui.backgroundColor   // 依然为nil

 优化代码:

enum Theme {
    case DayMode
    case NightMode
}

class UI {
    var fontColor: UIColor!
    var backgroundColor: UIColor!
    
    // 属性观察器
    var themeMode: Theme = .DayMode {
        didSet{
            self.changeTheme(themeMode)
        }
    }
    
    // 初始化方法
    init(themeMode: Theme) {
        self.themeMode = themeMode
        // 在初始化的方法中调用一下
        self.changeTheme(themeMode)
    }
    
    // 改变属性的方法
    func changeTheme(themeMode: Theme) {
        switch themeMode {
        case .DayMode:
            fontColor = UIColor.blackColor()
            backgroundColor = UIColor.whiteColor()
        case .NightMode:
            fontColor = UIColor.whiteColor()
            backgroundColor = UIColor.blackColor()
        }
    }
}

// 调用
let ui = UI(themeMode: .DayMode)
ui.themeMode         // DayMode
ui.fontColor         // blackColor
ui.backgroundColor   // whiteColor

4.延迟属性

class ColsedRange {
    let start: Int
    let end: Int

    // 计算型属性
    var width: Int {
        return end - start + 1
    }
    
    // 延迟性属性的声明
    // 闭包 -- 第一次调用sum时,才计算;后面的调用都不会调用计算闭包
    // lazy不可以用于let类型的属性
    lazy var sum: Int = {
        var res = 0
        for i in self.start...self.end {
            res += i
        }
        return res
    }()
    
    // 初始化方法
    init?(start: Int, end: Int) {
        guard start <= end else {
            return nil
        }
        self.start = start
        self.end = end
    }
}

// 调用
if let m = ColsedRange(start: 1, end: 9) {
    m.sum  // 45:第一次调用sum进行计算
    m.sum  // 45:第二次调用sum不会在进行重复计算,也就是不会重复调用闭包
}

5.访问控制关键字

 public:可以被模块外访问--公开的

 internal:可以被本模块访问(默认)

 private:可以被本文件访问--受保护的

enum Theme {
    case DayMode
    case NightMode
}

// 关键字:public
public class UI {
    private var fontColor: UIColor!
    private var backgroundColor: UIColor!
    
    // 属性观察器
    var themeMode: Theme = .DayMode {
        didSet{
            self.changeTheme(themeMode)
        }
    }
    
    // 初始化方法
    init(themeMode: Theme) {
        self.themeMode = themeMode
        // 在初始化的方法中调用一下
        self.changeTheme(themeMode)
    }
    
    // 关键字:private
    private func changeTheme(themeMode: Theme) {
        switch themeMode {
        case .DayMode:
            fontColor = UIColor.blackColor()
            backgroundColor = UIColor.whiteColor()
        case .NightMode:
            fontColor = UIColor.whiteColor()
            backgroundColor = UIColor.blackColor()
        }
    }
}

6.单例模式

// 单例声明文件
// 单例
public class GameManager {
    public var score = 0
    public static let defaultGameManager = GameManager()
    
    // 初始化方法是私有的,受保护的,只有本文件是可以调用的
    private init() {
        
    }
    
    public func addScore() {
        score += 10
    }
}

// 单例调用文件
// 调用1
let gameManager1 = GameManager.defaultGameManager
gameManager1.addScore()
gameManager1.score       // 10
// 调用2
let gameManager2 = GameManager.defaultGameManager
gameManager2.addScore()
gameManager2.score       // 20

 整个项目,单例只是产生一个对象

 

十四、继承和构造方法

1.继承

1)基本概念

 a.final:如果希望某一个类不再被其他类继承,则可以在class前面添加关键字final

 b.继承关系中,子类可以继承父类所有的属性和方法;但是对于父类不可以使用子类的属性和方法

2)代码实例

class Avatar {
    var name: String
    var life = 100
    var isAlive: Bool = true
    
    // 初始化方法
    init(name: String) {
        self.name = name
    }
    
    // 函数方法
    func beAttacked(attack: Int) {
        life -= attack
        if life <= 0 {
            isAlive = false
        }
    }
}

// User继承自Avatar(继承属性和方法)
class User: Avatar {
    var score = 0
    var level = 0
    
    // 函数方法
    func getScore(score: Int) {
        self.score += score
        if self.score > level * 100 {
            level += 1
        }
    }
}

// Magician继承自User
// final关键字(不希望该类再被继承)
final class Magician: User {
    var magic = 100
}

// 初始化一个User对象
let player = User(name: "Frank")
player.name
player.life
player.isAlive
player.score
player.beAttacked(20)
player.getScore(20)

// 初始化一个Magician对象
let magician = Magician(name: "Li Dong")
magician.name
magician.life
magician.isAlive
magician.score
magician.magic
magician.beAttacked(20)
magician.getScore(20)

2.多态

以下代码实例的继承关系图:

代码:

// 基类:Avatar
class Avatar {
    var name: String
    var life = 100            // 生命值
    var isAlive: Bool = true  // 是否阵亡
    
    // 初始化方法
    init(name: String) {
        self.name = name
    }
    
    // 函数方法:受到伤害(掉生命值)
    func beAttacked(attack: Int) {
        life -= attack
        if life <= 0 {
            isAlive = false
        }
    }
}

// User(玩家)继承自Avatar
class User: Avatar {
    var score = 0  // 游戏分数
    var level = 0  // 玩家等级
    
    // 函数方法:获取玩家分数和等级
    func getScore(score: Int) {
        self.score += score
        if self.score > level * 100 {
            level += 1
        }
    }
}

// Magician(魔术师)继承自User
final class Magician: User {
    var magic = 100
    
    // 函数方法:治愈玩家(玩家生命值加10)
    func heal(user: User) {
        user.life += 10
    }
}

// Warrior(战士)继承自User
final class Warrior: User {
    var weapon: String?
}

// Monster(怪兽)继承自Avatar
class Monster: Avatar {
    // 函数方法:攻击玩家掉生命值
    func attack(user: User, amount: Int) {
        user.beAttacked(amount)
    }
}

// Zombie(僵尸)继承自Monster
class Zombie: Monster {
    var type = "Default"
}

// 函数方法
func printBasicInfo(avatar: Avatar) {
    print("The avatar's name is \(avatar.name)")
    print("它的声明值是:\(avatar.life). \((avatar.isAlive) ? "活着" : "阵亡")")
    print("========")
}

// 多态的使用
let player1 = Magician(name: "Frank")
let player2 = Warrior(name: "Li Dong")
let zombie = Zombie(name: "default zombie")
let monster = Monster(name: "monster")

// 对所有的对象伤害为10
let avatarArr: [Avatar] = [player1, player2, zombie, monster]
for avatar in avatarArr {
    avatar.life -= 10
}

// Magician(魔法师)治愈玩家
player1.heal(player2)
player1.heal(player1)

3.重置

1)子类可以重置父类的属性和方法,子类重置该属性或方法时,需要用关键字override去修饰

2)父类也可以不允许子类去重置某一写特定的属性或方法,在父类中需要用关键字final去修饰这些特定的不可被子类重置的属性或方法

代码示例:

// 基类:Avatar
class Avatar {
    var name: String
    var life = 100 { // 生命值
        didSet{
            if self.life <= 0 {
                self.isAlive = false
            }
            if self.life > 100 {
                self.life = 100
            }
        }
    }
    var isAlive: Bool = true  // 是否阵亡
    var description: String {
        return "I'm Avatar \(self.name)."
    }
    
    
    // 初始化方法
    init(name: String) {
        self.name = name
    }
    
    // 函数方法:受到伤害(掉生命值)
    func beAttacked(attack: Int) {
        self.life -= attack
        if self.life <= 0 {
            self.isAlive = false
        }
    }
}

// User(玩家)继承自Avatar
class User: Avatar {
    var score = 0  // 游戏分数
    var level = 0  // 玩家等级
    
    // 重置属性
    // User中定义一个新的description来覆盖父类中的description
    override var description: String{
        return "I'm User \(self.name)."
    }
    
    // 不希望任何一个子类重置该方法
    // 函数方法:获取玩家分数和等级
    final func getScore(score: Int) {
        self.score += score
        if self.score > self.level * 100 {
            self.level += 1
        }
    }
}

// Warrior(战士)继承自User
final class Warrior: User {
    var weapon: String?
    
    // 重置方法
    // Warrior中声明一个新的beAttacked方法来覆盖User中的beAttacked
    override func beAttacked(attack: Int) {
        self.life -= attack / 2
        if self.life <= 0 {
            self.isAlive = false
        }
    }
}

4.构造函数

1)两段式构造函数

a.概念

在子类中的构造函数:

 1️⃣先初始化子类特有的属性

 2️⃣然后再去调用父类的构造函数初始化父类的属性

 3️⃣只能是对整个类构造完成,才可以用self去调用其他方法和属性,也就是在super的初始化方法之前,不能出现self的逻辑

b.代码示例

// 基类:Avatar
class Avatar {
    var name: String
    
    // 初始化方法
    init(name: String) {
        self.name = name
    }
}

// User(玩家)继承自Avatar
class User: Avatar {
    var score = 0  // 游戏分数
    var level = 0  // 玩家等级
    var group: String
    
    // 在子类中的构造函数(两段式构造原则)
    // 1.先初始化子类特有的属性
    // 2.然后再去调用父类的构造函数初始化父类的属性
    // 3.只能是对整个类构造完成才可以用self去调用其他方法和属性
    init(name: String, group: String){
        self.group = group
        // 调用父类的初始化方法
        super.init(name: name)
    }
}

2)便利构造函数和指定构造函数

a.概念

1️⃣关键字:convenience修饰便利构造函数

2️⃣便利构造函数只能调用自己的构造函数,不能去调用父类的构造函数;调用父类构造函数的称之为指定构造函数

3️⃣便利构造函数中必须要调用一个指定构造函数,只有指定构造函数才可以调用父类的构造函数

b.代码示例

// 基类:Avatar
class Avatar {
    var name: String
    
    // 初始化方法
    init(name: String) {
        self.name = name
    }
}

// User(玩家)继承自Avatar
class User: Avatar {
    var score = 0  // 游戏分数
    var level = 0  // 玩家等级
    var group: String
    
    // 在子类中的构造函数(两段式构造原则)
    // 1.先初始化子类特有的属性
    // 2.然后再去调用父类的构造函数初始化父类的属性
    // 3.只能是对整个类构造完成才可以用self去调用其他方法和属性
    init(name: String, group: String){
        self.group = group
        // 调用父类的初始化方法
        super.init(name: name)
    }
}

final class Warrior: User {
    var weapon: String
    
    // 指定构造函数(参数有默认值)
    init(name: String, group: String, weapon: String = "Sword") {
        self.weapon = weapon
        // 调用super构造方法
        super.init(name: name, group: group)
    }

    // 父类的构造函数可以被重载
    override init(name: String, group: String) {
        let weapon = "Sword"
        self.weapon = weapon
        super.init(name: name, group: group)
    }
    
    // 便利构造函数
    convenience init(weapon: String = "Sword") {
        let name: String = "Frank"
        let group: String = "ld"
        self.init(name: name, group: group, weapon: weapon)
    }
}

3)构造函数的继承

a.两个原则

1️⃣如果子类没有实现任何父类的指定构造函数,则自动继承父类的所有指定构造函数

2️⃣如果子类实现了父类所有的指定构造函数,则自动继承父类的所有便利构造函数

b.代码示例

 原则一:

// 基类User(玩家)
class User {
    var name: String
    var group: String
    
    // 指定构造函数
    init(name: String, group: String){
        self.group = group
        self.name = name
    }
    
    // 便利构造函数
    convenience init(group: String = "") {
        let name = "Frank"
        self.init(name: name, group: group)
    }
}

// Warrior(战士)继承自User
final class Warrior: User {
    // 便利构造函数
    convenience init() {
        let name = "Frank"
        let group = "One"
    
        // 原则一:如果子类没有实现任何父类的指定构造函数,则自动继承父类的所有指定构造函数
        self.init(name: name, group: group)
    }
}

 原则二:

// 基类User(玩家)
class User {
    var name: String
    var group: String
    
    // 指定构造函数
    init(name: String, group: String){
        self.group = group
        self.name = name
    }
    
    // 便利构造函数
    convenience init(group: String = "") {
        let name = "Frank"
        self.init(name: name, group: group)
    }
}

// Warrior(战士)继承自User
final class Warrior: User {
    var weapon: String
    
    // 指定构造函数
    init(name: String, group: String, weapon: String) {
        self.weapon = weapon
        // 调用super构造方法
        super.init(name: name, group: group)
    }
    
    // 便利构造函数 -- 重载父类的指定构造函数
    convenience override init(name: String, group: String) {
        let weapon = "Sword"
        self.init(name: name, group: group, weapon: weapon)
    }
}

// 原则二:如果子类实现了父类所有的指定构造函数,则自动继承父类的所有便利构造函数
var warrior1 = Warrior(name: "Frank", group: "LD", weapon: "Dong")
var warrior2 = Warrior(name: "Frank", group: "LD")
var warrior3 = Warrior(group: "Dong") // 父类的便利构造函数

4)必须实现的构造函数

a.概念

1️⃣用关键字required修饰

2️⃣确定某一个类会成为父类,要求继承该类的所有子类都必须实现的构造方法可以使用关键字required来修饰

3️⃣required修饰的方法在子类中也必须使用required修饰,不可以使用重载(override)来修饰,因为已经没有意义

b.代码示例

// 基类:Avatar
class Avatar {
    var name: String

    // 指定构造函数
    // required关键字修饰(子类必须实现的构造函数)
    required init(name: String) {
        self.name = name
    }
    
    // 便利构造函数
    convenience init(firstName: String, laseName: String) {
        self.init(name: firstName + " " + laseName)
    }
}

// User(用户)继承自Avatar
class User: Avatar {
    var group: String
    
    // 指定构造函数
    init(name: String, group: String) {
        self.group = group
        super.init(name: name)
    }
    
    // 便利构造函数
    convenience init(group: String) {
        self.init(name: "Frank", group: group)
    }
    
    // 便利构造函数
    // 必须实现父类的构造函数,同时也需要用关键字required去修饰
    convenience required init(name: String) {
        self.init(name: "Frank", group: "One")
    }
}

 

十五、文档注释

1.文档注释基础和Mark Down

1)语法

a.普通的单行注释为双斜杠(//);文档单行注释为是三个斜杠(///),另外需要注意文档多行注释也可以使用三个斜杠,只不过行与行之间还应该添加三个斜杠隔开

b.普通的多行注释为/* */,且行与行之间不需要空行隔开;文档多行注释为是/** */,且行与行之间需要空行隔开

c.文档注释中可使用减号或加号或星号加空格(- )(+ )(* )的形式开头,编辑无序列表

d.文档注释中可使用阿拉伯数字加点号加空格(1. )的形式开头,编辑有序列表

e.文档注释中以三个`符号开头、三个`符号结尾(``` ```),编辑文档中的代码

f.文档注释中的标题可以使用#加空格(# )的形式开头,且可以根据#的个数来调节标题字体大小

g.文档注释中使用星号(*)或下划线(_)来包裹斜体字样

h.文档注释中使用两个星号(**)来包裹粗体字样

i.文档注释中使用中括号来包裹超链接,后面紧跟小括号包裹超链接地址

j.文档注释中使用感叹号加中括号来包裹图片超链接,后面紧跟小括号包裹图片地址,这样就在文档注释中加载了一张图片

2)代码讲解

a.案例一

/**
 第一行注释--第一行注释--第一行注释
 
 第二行注释--第二行注释--第二行注释
 
 - 减号标示的无序列表1
 - 减号标示的无序列表2
 - 减号标示的无序列表3
 
 + 加号标示的无序列表1
 + 加号标示的无序列表2
 + 加号标示的无序列表3
 
 * 星号标示的无序列表1
 * 星号标示的无序列表2
 * 星号标示的无序列表3
 
 1. 有序列表1
 2. 有序列表2
 3. 有序列表3
 
 ```
 let a = "Hello"
 let b = "Swift"
 ```
 
 */
func showMutilineComments() -> String {
    let text = "You can use the /** ... */ for multiline comments"
    return text
}

b.案例二

/// 第一行注释--第一行注释--第一行注释
///
/// 第二行注释--第二行注释--第二行注释
func showSingComments() -> String {
    let text = "You can use the /** ... */ for sing comments"
    print(text)
    return text
}

c.案例三

/**
 # 第一号标题
 ## 第二号标题
 ### 第三号标题
 斜体字样使用星号 *Frank&Frank* 或者使用下划线 _Frank&Frank_
 
 粗体字样使用两个星号 **粗体**
 
 这是个[超链接--点击进入百度首页](http://www.baidu.com)
 
 这是个![图片](http://image.baidu.com/search/detail?ct=503316480&z=0)
 */
func showComments() -> String {
    let text = "You can use the /** ... */ for comments"
    return text
}

2.文档注释中的另外三个域(Parameters/Returns/Throws)

1)参数域:Parameters

a.第一种语法:每个参数前分别加Parameter修饰

/// There are a few keywords Xcode can recognize automatically.
/// - Parameter item1: 参数1
/// - Parameter item2: 参数2
func showkeywordsCommentsWithItem(item1: AnyObject?, item2: AnyObject?) -> String {
    let text = "There are a few keywords Xcode can recognize automatically."
    return text
}

b.第二种语法:Parameter修饰

/// There are a few keywords Xcode can recognize automatically.
/// - Parameters:
///     - item1: 参数1
///     - item2: 参数2
func showkeywordsCommentsWithItem(item1: AnyObject?, item2: AnyObject?) -> String {
    let text = "There are a few keywords Xcode can recognize automatically."
    return text
}

结果:

2)返回值域和抛出异常:Returns/Throws

/// There are a few keywords Xcode can recognize automatically.
/// - Parameters:
///     - item1: 参数1
///     - item2: 参数2
/// - Returns: 返回值为String类型
/// - Throws: 抛出异常的信息
func showkeywordsCommentsWithItem(item1: AnyObject?, item2: AnyObject?) throws -> String {
    let text = "There are a few keywords Xcode can recognize automatically."
    return text
}

3.文档注释中的三个关键字(MARK/TODO/FIXME)

 

十六、下标和运算符重载

1.下标

1)概念

 1️⃣下标在Swift中很多数据类型中很常见,例如数组通过下标取值;在Swift的某些类型中,若想强制使用下标,得用到关键字subscript

 2️⃣关键字subscript可以看做是一个函数

2)结构体中的下标

 一维下标:

// 结构体
struct Vector {
    var x: Double = 0.0
    var y: Double = 0.0
    var z: Double = 0.0
    
    // 默认情况下,只有get方法--只可以通过下标读值,不可以通过下标写值
    subscript(index: Int) -> Double? {
        switch index {
        case 0: return x
        case 1: return y
        case 2: return z
        default: return nil
        }
    }
    
    // set和get
    subscript(axis: String) -> Double? {
        //
        set{
            guard let newValue = newValue else {
                return
            }
            switch axis {
            case "x", "X": x = newValue
            case "y", "Y": y = newValue
            case "z", "Z": z = newValue
            default: return
            }
        }
        
        //
        get{
            switch axis {
            case "x", "X": return x
            case "y", "Y": return y
            case "z", "Z": return z
            default: return nil
            }
        }
    }
}

// 通过下标取值
var vector1 = Vector(x: 1.0, y: 2.0, z: 3.0)
vector1[0]
vector1["x"]

// 通过下标写值
var vector2 = Vector(x: 1.0, y: 2.0, z: 3.0)
// vector2[0] = 300 (报错:只读,只有get方法)
vector2["x"] = 300

 多维下标:

// 结构体
struct Matrix {
    var data: [[Double]]
    let row: Int
    let col: Int
    
    init(row: Int, col: Int) {
        self.row = row
        self.col = col
        data = [[Double]]()
        for _ in 0 ..< row {
            let aRow = Array(count: col, repeatedValue: 0.0)
            data.append(aRow)
        }
    }
    
    // 二维下标 m[1, 1]
    subscript(x: Int, y: Int) -> Double {
        // set可写
        set{
            // assert:断言
            assert(x >= 0 && x < row && y >= 0 && y < col, "下标越界")
            data[x][y] = newValue
        }
        
        // get可读
        get{
            // assert:断言
            assert(x >= 0 && x < row && y >= 0 && y < col, "下标越界")
            return data[x][y]
        }
    }
    
    // 二维下标 m[1][1]
    subscript(row: Int) -> [Double] {
        // set可写
        set(vector){
            assert(vector.count == self.col, "当前行的元素个数不对")
            data[row] = vector
        }
        
        // get可读
        get{
            assert(row >= 0 && row < self.row, "下标越界")
            return data[row]
        }
    }
}

// 创建对象
var m = Matrix(row: 3, col: 3)
m[1, 1]
m[1, 1] = 200
// m[4, 4] (报错:断言,所以可以判断下标越界)
m[1][1]
m[1] = [400, 500, 600]
// m[1] = [400, 500, 600, 600] (报错:断言,所以可以判断当前行元素个数是否相符)

2.运算符重载

1)概念

 a.运算符重载分为:系统运算符重载、自定义运算符重载 

 b.运算符重载,其实就相当于重写运算符函数

 c.运算符重载不可以重载赋值运算符

 d.对于自定义运算符而言,重载必须向系统说明自定义运算符的信息,例如:结合性、优先级

  1️⃣自定义运算符说明采用关键字infix operator

  2️⃣自定义运算符说明中的大括号里面可以添加说明结合性和优先级(associativity:结合性,有左右之分,默认是没有结合性;precedence:优先级,默认为140,与加号同级)

  3️⃣对于一些有结合性之分的自定义运算符,如果没有指定结合性,就会报错

infix operator ^ {
    associativity left  
    precedence 140
}
func ^ (x: Double) -> Double {
    return x
}

2)代码

// 结构体
struct Vector {
    var x: Double = 0.0
    var y: Double = 0.0
    var z: Double = 0.0
}

// 重载系统的运算符方法
// 1.重载+方法
func + (left: Vector, right: Vector) -> Vector {
    return Vector(x: left.x + right.x, y: left.y + right.y, z: left.z + right.z)
}

// 2.重载-方法
func - (left: Vector, right: Vector) -> Vector {
    return Vector(x: left.x - right.x, y: left.y - right.y, z: left.z - right.z)
}

// 3.重载*方法(两个向量相乘)
func * (left: Vector, right: Vector) -> Double {
    return left.x * right.x + left.y * right.y + left.z * right.z
}

// 4.重载*方法(向量乘以某一个数)
func * (left: Vector, a: Double) -> Vector {
    return Vector(x: left.x * a, y: left.y * a, z: left.z * a)
}

// 5.重载+=方法
func += (inout left: Vector, right: Vector) {
    left = left + right
}

// 6.重载负号方法(重载负号一定要加上关键字prefix)
prefix func - (vector: Vector) -> Vector {
    return Vector(x: -vector.x, y: -vector.y, z: -vector.z)
}

// 7.重载==
func == (left: Vector, right: Vector) -> Bool {
    return left.x == right.x && left.y == right.y && left.z == right.z
}

// 8.重载!=
func != (left: Vector, right: Vector) -> Bool {
    return left.x != right.x || left.y != right.y || left.z != right.z
    // return !(left == right)
}

// 9.重载<
func < (left: Vector, right: Vector) -> Bool {
    if left.x != right.x {
        return left.x < right.x
    }
    
    if left.y != right.y {
        return left.y < right.y
    }
    
    if left.z != right.z {
        return left.z < right.z
    }
    
    return false
}

// 10.重载<=
func <= (left: Vector, right: Vector) -> Bool {
    return left < right || left == right
}

// 11.重载>
func > (left: Vector, right: Vector) -> Bool {
    return !(left <= right)
}

// 12.重载>=
func >= (left: Vector, right: Vector) -> Bool {
    return !(left < right)
}

// 自定义运算符重载
// 1.单目运算符
// 1)前置+++
prefix operator +++ {}
prefix func +++ (inout vector: Vector) -> Vector {
    vector += Vector(x: 1.0, y: 1.0, z: 1.0)
    return vector
}

// 2)后置+++
postfix operator +++ {}
postfix func +++ (inout vector: Vector) -> Vector {
    let ret = vector
    vector += Vector(x: 1.0, y: 1.0, z: 1.0)
    return ret
}

// 2.双目运算符用infix
// 1)重载自定义运算符^(计算两个向量的夹角)
// 属性:associativity(结和性:默认是没有结合性)
// 属性:precedence(优先级:默认与加号一样)
infix operator ^ {
    associativity left
    precedence 140
}
func ^ (left: Vector, right: Vector) -> Double {
    let a = sqrt(left.x * left.x + left.y * left.y + left.z * left.z)
    let b = sqrt(right.x * right.x + right.y * right.y + right.z * right.z)
    return acos((left * right) / (a * b))
}

// 2)重载自定义运算符**
infix operator ** {
    associativity right
    precedence 155
}
func ** (x: Double, n: Double) -> Double {
    return pow(x, n)
}

// 创建两个结构体对象
var vector1 = Vector(x: 1.0, y: 2.0, z: 3.0)
var vector2 = Vector(x: 1.0, y: 2.0, z: 3.0)
// 运算
vector1 + vector2
vector1 - vector2
vector1 * vector2
-vector1
vector1 * 2
vector1 += vector2
vector1 == vector2
vector1 != vector2
vector1 < vector2
vector1 <= vector2
vector1 > vector2
vector1 >= vector2
+++vector1
vector1+++
vector1 ^ vector2
2 ** 3 ** 2      // 由于右结合性,值为:512
2 * 2 ** 3 ** 2  // 由于优先级为155,乘号优先级为150,所以值为:1024

 

十七、扩展(Extension)和泛型(Generic)

1.扩展

1)概念

Swift中的Extension与OC中的Category差不多:

 1️⃣OC中的Category需要重新起一个文件名;Swift中只需要用关键字extension去声明

 2️⃣OC中的扩展只可以扩展方法,不可以扩展属性;Swift中可以扩展方法,也可以扩展属性,但是属性必须是计算性属性

2)代码

 结构体扩展:

struct Point {
    var x = 0.0
    var y = 0.0
}

struct Size {
    var width = 0.0
    var height = 0.0
}

// 定义一个结构体Rectangle
struct Rectangle {
    var origin = Point()
    var size = Size()
    
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }
}

// 对结构体Rectangle扩展
extension Rectangle {
    // 为结构体Rectangle扩展一个方法
    // 注意:由于结构体是引用类型,所以如果要改变结构体自身的值,必须使用mutating
    mutating func translate(x: Double, y: Double) {
        self.origin.x += x
        self.origin.y += y
    }
}

 类扩展:

struct Point {
    var x = 0.0
    var y = 0.0
}

struct Size {
    var width = 0.0
    var height = 0.0
}

// 定义一个类Rectangle
class Rectangle {
    var origin = Point()
    var size = Size()
    
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }
}

// 对类Rectangle扩展方法
extension Rectangle {
    // 为类Rectangle扩展一个方法
    func translate(x: Double, y: Double) {
        self.origin.x += x
        self.origin.y += y
    }
}

// 对类Rectangle扩展属性(只能是计算性属性),同时也可以为这个属性扩展构造函数(必须是便利构造函数)
extension Rectangle {
    // 扩展中的属性必须是计算性属性
    var center: Point{
        set(newCenter){
            origin.x = newCenter.x - size.width / 2.0
            origin.y = newCenter.y - size.height / 2.0
        }
        
        get{
            let centerX = origin.x + size.width / 2.0
            let centerY = origin.y + size.height / 2.0
            return Point(x: centerX, y: centerY)
        }
    }
    
    // 扩展属性的构造函数必须是便利构造函数
    convenience init(center: Point, size: Size) {
        let originX = center.x - size.width / 2.0
        let originY = center.y - size.height / 2.0
        let origin = Point(x: originX, y: originY)
        self.init(origin: origin, size: size)
    }
}

 扩展嵌套类型:

struct Point {
    var x = 0.0
    var y = 0.0
}

struct Size {
    var width = 0.0
    var height = 0.0
}

// 定义类Rectangle
class Rectangle {
    var origin = Point()
    var size = Size()
    
    // 构造函数
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }
}

// 扩展类Rectangle
extension Rectangle {
    
    // 扩展的嵌套类型
    enum Vertex: Int {
        case LeftTop   // 默认为0
        case RightTop
        case LeftBottom
        case RightBottom
    }
    
    // 扩展方法
    func pointAtVertex(v: Vertex) -> Point {
        switch v {
        case .LeftTop:
            return origin
        case .RightTop:
            return Point(x: origin.x + size.width, y: origin.y)
        case .LeftBottom:
            return Point(x: origin.x, y: origin.y + size.height)
        case .RightBottom:
            return Point(x: origin.x, y: origin.y + size.height)
        }
    }
}

// 初始化一个矩形rect
let rect = Rectangle(origin: Point(), size: Size(width: 4, height: 3))
// Rectangle.Vertex.RightBottom 嵌套的枚举类型
rect.pointAtVertex(Rectangle.Vertex.RightBottom)  // x = 4,y = 3

 扩展下标:

struct Point {
    var x = 0.0
    var y = 0.0
}

struct Size {
    var width = 0.0
    var height = 0.0
}

// 定义类Rectangle
class Rectangle {
    var origin = Point()
    var size = Size()
    
    // 构造函数
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }
}

// 扩展类Rectangle
extension Rectangle {
    
    // 扩展的嵌套类型
    enum Vertex: Int {
        case LeftTop   // 默认为0
        case RightTop
        case LeftBottom
        case RightBottom
    }
    
    // 扩展方法
    func pointAtVertex(v: Vertex) -> Point {
        switch v {
        case .LeftTop:
            return origin
        case .RightTop:
            return Point(x: origin.x + size.width, y: origin.y)
        case .LeftBottom:
            return Point(x: origin.x, y: origin.y + size.height)
        case .RightBottom:
            return Point(x: origin.x, y: origin.y + size.height)
        }
    }
    
    // 扩展下标
    subscript(index: Int) -> Point {
        assert(index >= 0 && index < 4, "下标越界")
        let v: Vertex = Vertex(rawValue: index)!
        return pointAtVertex(v)
    }
}

// 初始化一个矩形rect
let rect = Rectangle(origin: Point(), size: Size(width: 4, height: 3))
rect[2]    // x = 4, y = 3

 扩展标准库:

extension Int {
    // 扩展计算型属性(平方)
    var square: Int {
        return self * self
    }
    
    // 扩展计算型属性(立方)
    var cube: Int {
        return self * self * self
    }
    
    // 扩展方法:判断某一整型是否在区间[left, right)
    func inRange(closedLeft left: Int, openedRight: Int) -> Bool {
        return self >= left && self < openedRight
    }
    
    // 扩展方法:传参为一个函数类型
    func repetitions(task: () -> Void) {
        for _ in 0 ..< self {
            task()
        }
    }
    
    // 扩展方法
    func stride(to end: Int, by stride: Int, task: (Int) -> Void) {
        for i in self.stride(to: end, by: stride) {
            task(i)
        }
    }
}

// 通过扩展的方法,计算num的平方和立方
let num = 8
num.square  // 64
num.cube    // 512

// 通过扩展的方法,判断index是否在区间[0, 20)
let index = 12
index.inRange(closedLeft: 0, openedRight: 20)

// 通过扩展的方法,做n次循环操作
let n = 3
n.repetitions {
    print("Frank!")
}

// 关于for循环的一点说明:
// 1.在Swift3中将彻底弃用for循环的常规写法(含有步长的三步式),只允许用for-in语句
// 2.如果需要使用步长,则需要用到for-in循环的下面两种形式:
// for i in a.stride(to: b, by: c)       说明:a <= i < b 步长为c
// for i in a.stride(through: b, by: c)  说明:a <= i <= b 步长为c

// 前闭后开区间:[2, num) -- 步长为3
for i in 2.stride(to: num, by: 3) {
    print(i)
}

// 闭区间:[2, num] -- 步长为3
for i in 2.stride(through: num, by: 3) {
    print(i)
}

// 通过扩展的方法,进行循环操作
num.stride(to: 20, by: 8) { (index) in
    print(index)
}

2.泛型

1)概念

a.泛型函数:泛型放在函数中使用

b.泛型类型:泛型放在类型中使用

2)代码

 泛型函数:

// 方法:交换两个东西
// a和b都是泛型,不强调类型
func swapTwoThing<T>(inout a: T, inout b: T) {
    (a, b) = (b, a)
}

// 函数调用
var a: Int = 0
var b: Int = 2
swapTwoThing(&a, b: &b)

var m = "Hello"
var n = "Frank"
swapTwoThing(&m, b: &n)

 泛型类型:

// 定义一个栈(后进先出)
struct Stack<T> {
    var items = [T]()
    
    // 方法:判断栈是否为空
    func isEmpty() -> Bool {
        return items.count == 0
    }
    
    // 方法:压栈
    mutating func push(item: T) {
        items.append(item)
    }
    
    // 方法:出栈
    mutating func pop() -> T? {
        guard !self.isEmpty() else {
            return nil
        }
        // 移除并返回最后一个元素
        return items.removeLast()
    }
}

// 扩展一个泛型
extension Stack {
    // 扩展方法:查看栈顶的元素
    func top() -> T? {
        return items.last
    }
}

// 定义一个拥有两个泛型的变量Pair
struct Pair<T1, T2> {
    var a: T1
    var b: T2
}

// 泛型类型的使用
var s = Stack<Int>()
s.push(2)
s.push(4)
s.pop()

var ss = Stack<String>()
ss.push("Frank")
ss.push("Hello")
ss.pop()

var sss = Stack<Pair<String, Int>>()
var pair1 = Pair<String, Int>(a: "Frank", b: 500)
var pair2 = Pair<String, Int>(a: "LiDong", b: 300)
sss.push(pair1)
sss.push(pair2)
sss.pop()

 

十八、协议 

1.协议基础

1)说明

a.协议中的方法,只能有声明,不能去实现

b.协议中的属性,不能有默认值,且变量类型必须是var

c.协议如果在结构体中实现,那么协议中那些改变自身的方法需要加上关键字mutating;但是如果在类中实现,那么由于类是引用类型,所以不需要

d.如果指明协议只能被类去遵循,而不能被其他的(如:结构体)遵循,那么我们可以在定义协议的时候指定它为class即可

2)代码

 结构体中的协议:

protocol Pet {
    // 协议中的属性
    var name: String {get set}
    var birthPlace: String {get}

    // 协议中的方法
    func playWith()
    func fed(food: String)
    // 注:结构体实现协议中改变自身的方法,必须使用mutating
    mutating func changeName(newName: String)
}

// 定义一个结构体类型Dog遵循协议Pet
struct Dog: Pet {
    // 必须存在的协议中的属性
    var name: String = "XiaoHuang"
    var birthPlace: String = "HG"

    // 实现协议中的方法
    func playWith() {
        print("Wong!")
    }
    func fed(food: String) {
        if food == "Booe" {
            print("Happy")
        } else {
            print("I want a bone")
        }
    }
    mutating func changeName(newName: String) {
        name = newName
    }

    // 当然了,除了协议的属性和方法,其本身也可以有自己的属性和方法
    // ......
}

let myDog: Dog = Dog()
let aPet: Pet = myDog

 类中的协议:

// 定义一个协议Pet(只能被类遵循)
protocol Pet: class {
    // 协议中的属性
    var name: String {get set}
    var birthPlace: String {get}
    
    // 协议中的方法
    func playWith()
    func fed(food: String)
    func changeName(newName: String)
}

// 定义一个类Dog遵循协议Pet
class Dog: Pet {
    // 必须存在的协议中的属性
    var name: String = "XiaoHuang"
    var birthPlace: String = "HuangGang"
    
    // 实现协议中的方法
    func playWith() {
        print("Wong!")
    }
    func fed(food: String) {
        if food == "Booe" {
            print("Happy")
        } else {
            print("I want a bone")
        }
    }
    func changeName(newName: String) {
        name = newName
    }
    
    // 当然了,除了协议的属性和方法,其本身也可以有自己的属性和方法
    // ......
}

var myDog: Dog = Dog()
myDog.birthPlace = "BeiJing"

var aPet: Pet = myDog
// aPet.birthPlace = "HuangGang" // 报错:因为birthPlace是只读类型

2.协议和构造函数

// 定义一个协议Pet(只能被类遵循)
protocol Pet: class {
    // 协议中的属性
    var name: String {get set}
    
    // 协议中的方法
    // 协议中也可以声明构造函数
    init(name: String)
    func playWith()
    func fed(food: String)
}

// 定义一个类Animal
class Animal {
    var type: String = "mammal"
}

// 定义一个类Dog继承自Animal且遵循协议Pet
class Dog: Animal, Pet {
    // 必须存在协议中的属性
    var name: String = "Puppy"
    
    // 必须实现协议的方法
    // 协议中的构造函数在实现的时候必须注意以下两点:
    // 1.协议中的构造函数在该类和该类的所有子类中都必须实现,所以使用required
    // 2.当然了,如果该类被final修饰,则表示该类不会被继承,所以也就用不着required
    required init(name: String) {
        self.name = name
    }
    func playWith() {
        print("Wong!")
    }
    func fed(food: String) {
        if food == "Booe" {
            print("Happy")
        } else {
            print("I want a bone")
        }
    }
}

// 定义一个类Bird继承自Animal
class Bird: Animal {
    var name: String = "Little Little Bird"
    
    init(name: String) {
        self.name = name
    }
}

// 定义一个类Parrot继承自Bird且遵循协议Pet
class Parrot: Bird, Pet {
    // 实现协议中的方法
    // 构造函数说明:
    // 1.override(重载):Parrot继承Bird,重载Bird的构造函数
    // 2.required(必须实现):Parrot遵循协议Pet,本身和其子类中必须实现该构造函数
    required override init(name: String) {
        super.init(name: name + " " + name)
    }
    func playWith() {
        print("Wong!")
    }
    func fed(food: String) {
        if food == "Booe" {
            print("Happy")
        } else {
            print("I want a bone")
        }
    }
}

3.类型别名(typealias)和关联类型(associatedtype)

1)类型别名(typealias)

// 取别名(Length相当于Double)
typealias Length = Double
// 扩展标准库Double
extension Double {
    // 所有单位转化为米
    var km: Length { return self * 1000.0 }
    var m: Length { return self }
    var cm: Length { return self / 100.0 }
    var ft: Length { return self / 3.28084 }
}

// 3.54km = 3540m
let runningDistance: Length = 3.54.km  // 3540

2)协议中的关联类型(associatedtype)

// 定义一个协议WeightCalculable
protocol WeightCalculable {
    // associatedtype:为协议关联一个类型
    associatedtype WeightType
    var weight: WeightType { get }
}

// 定义一个类Dog遵守协议WeightCalculable
class Dog: WeightCalculable {
    // 取别名(WeightType相当于Double)
    typealias WeightType = Double
    var weight: WeightType = 0
}

// 定义一个类Ship遵守协议WeightCalculable
class Ship: WeightCalculable {
    // 取别名(WeightType相当于Int)
    typealias WeightType = Int
    let weight: WeightType
    
    init(weight: WeightType) {
        self.weight = weight
    }
}

// 扩展标准库Int
extension Int {
    typealias Weight = Int
    // 千克转化为吨
    var t: Weight { return self / 1000 }
}

// 千克转化为吨
let titanic = Ship(weight: 4836.t)

4.标准库中的常用协议

// 定义一个结构体Record遵守以下协议
struct Record: Equatable, Comparable, CustomStringConvertible, BooleanType {
    var wins: Int
    var losses: Int
    
    // 遵守协议CustomStringConvertible,必有计算型属性description
    var description: String {
        // 返回自定义打印信息格式
        return "WINS: " + String(wins) + ", LOSSES: " + String(losses)
    }
    
    // 遵守协议BooleanType,必有计算型属性boolValue
    var boolValue: Bool {
        return wins > losses
    }
}

// 遵守协议Equatable,必须重载运算符(==)
// 运算符重载应该放在类型定义外面,对于协议一样
// 另外由于遵守了协议Equatable,编译器会自动检测类型定义完成之后是否重载运算符(==),所以这个之间不得有其他的任何代码
func == (lhs: Record, rhs: Record) -> Bool {
    return lhs.wins == rhs.wins && lhs.losses == rhs.losses
}

// 遵守协议Comparable,必须重载运算符(<)
func < (lhs: Record, rhs: Record) -> Bool {
    if lhs.wins != rhs.wins {
        return lhs.wins < rhs.wins
    }
    return lhs.losses > rhs.losses
}

// 1.协议Equatable
let recordA = Record(wins: 10, losses: 5)
let recordB = Record(wins: 10, losses: 5)
// 只有当遵守协议Equatable,并且重载运算符(==),!=才有效;否则报错
recordA != recordB

// 2.协议Comparable
// 1)比较运算符与协议Comparable
let recordC = Record(wins: 10, losses: 5)
let recordD = Record(wins: 10, losses: 5)
// 只有当遵守协议Comparable,并且重载运算符(<),其他比较运算符才有效;否则报错
recordC > recordD
recordC < recordD
recordC >= recordD
recordC <= recordD
// 2)sort()函数与协议Comparable
var teamRecord1 = Record(wins: 10, losses: 3)
var teamRecord2 = Record(wins: 8, losses: 10)
var teamRecord3 = Record(wins: 3, losses: 16)
var records = [teamRecord1, teamRecord2, teamRecord3]
// 对于sort()函数,它认为对于它的每一个元素(对象)都必须是遵守了Comparable协议,也就是可比较的;否则报错
records.sort()
records.sort(<) // 升序
records.sort(>) // 降序

// 3.协议CustomStringConvertible
let recordE = Record(wins: 10, losses: 5)
print(recordA)    // WINS: 10, LOSSES: 5

// 4.协议BooleanType
// 1)boolValue属性的调用
let recordF = Record(wins: 10, losses: 5)
if recordF {
    print("Great!")
}
// 2)Int类型作为bool值
// Swift对于Int类型做bool值是报错的,但如果对Int类扩展且遵守协议BooleanType就OK啦
extension Int: BooleanType {
    public var boolValue: Bool {
        return self != 0
    }
}
var wins = 0
if !wins {
    print("从来没有赢过!")
}

5.面向协议编程

1)协议扩展

 协议的扩展需要注意:

  a.协议扩展可以扩展属性,也可以扩展方法

  b.协议扩展既可以扩展自定义协议,也可以扩展系统协议

  c.协议扩展中扩展的属性和方法允许有实现部分,该实现为默认情况

  d.遵循该协议的类或结构体可以对协议扩展已经存在实现的属性和方法,不再重复实现,那么此时的类或结构体对这些属性和方法就采用扩展中的默认实现;但是如果有重复实现,就会以该类或结构体中的实现为主

  e.协议也可以被另外的协议继承

  f.扩展协议也可以加一些条件,比如:扩展协议Record,这个扩展仅仅只对遵循了协议Tieable的类或结构体有效,这时我们就可以用where语句:extension Record where Self: Tieable { /* 具体的扩展内容 */ }

2)面向协议编程

 苹果推出Swift语言,同时宣称Swift是一门面向一些编程的语言。所谓面向协议编程,就是将协议作为一个类型来使用

3)代码实例

// 自定义一个协议Record,该协议继承系统协议CustomStringConvertible
protocol Record: CustomStringConvertible {
    // 协议中的属性
    var wins: Int {get}      // 胜局
    var losses: Int {get}    // 败局
}

// 自定义一个协议Tieable
protocol Tieable {
    // 协议中的属性
    var ties: Int {get}      // 平局
}

// 扩展协议Record
extension Record {
    // 扩展属性(计算型属性)
    var description: String {
        return String(format: "WINS: %d, LOSSES: %d", [wins, losses])
    }
    var gamePlayed: Int {
        return wins + losses  // 比赛总场次
    }
    
    // 扩展方法
    func winningPercent() -> Double {
        return Double(wins) / Double(gamePlayed)  // 胜率
    }
}

// 扩展协议Record,且只对于遵循协议Tieable的类
extension Record where Self: Tieable {
    // 扩展属性(计算型属性)
    var description: String {
        return String(format: "WINS: %d, LOSSES: %d, TIES: %d", [wins, losses, ties])
    }
    var gamePlayed: Int {
        return wins + losses + ties  // 比赛总场次
    }
    
    // 扩展方法
    func winningPercent() -> Double {
        return Double(wins) / Double(gamePlayed) // 胜率
    }
}

// 扩展系统协议CustomStringConvertible
// 协议扩展除了自定义协议外,还可以扩展系统的协议
extension CustomStringConvertible {
    // 扩展的属性(计算型属性)
    var descriptionWithDate: String {
        return NSDate().description + " " + description
    }
}

// 自定义一个结构体BasketballRecord遵循协议Record
struct BasketballRecord: Record {
    // 必须有的协议属性
    var wins: Int
    var losses: Int
}

// 自定义一个结构体BaseballRecord遵循协议Record
struct BaseballRecord: Record {
    // 必须有的协议属性
    var wins: Int
    var losses: Int
    // 覆盖协议Record扩展中的扩展属性(计算型属性)的默认实现
    let gamePlayed: Int = 162
}

// 自定义一个结构体FootballRecord遵循协议Record和Tieable
struct FootballRecord: Record, Tieable {
    // 必须有的协议属性
    var wins: Int
    var losses: Int
    var ties: Int
}

// 实例化
let basketballTeamRecord = BasketballRecord(wins: 2, losses: 2)
basketballTeamRecord.wins                // 2
basketballTeamRecord.gamePlayed          // 4
basketballTeamRecord.winningPercent()    // 0.5

let baseballTeamRecord = BaseballRecord(wins: 6, losses: 4)
baseballTeamRecord.wins                  // 6
baseballTeamRecord.gamePlayed            // 162
baseballTeamRecord.winningPercent()      // 0.037037037...

let footballTeamRecord = FootballRecord(wins: 2, losses: 2, ties: 2)
footballTeamRecord.wins                  // 2
footballTeamRecord.gamePlayed            // 6
footballTeamRecord.winningPercent()      // 0.33333333...

6.协议聚合

1)概念

 所谓协议聚合,指的是同时遵守多个协议,语法:protocol<Prizable, CustomStringConvertible>

2)代码示例

// 自定义一个协议Record,该协议继承系统协议CustomStringConvertible
protocol Record: CustomStringConvertible {
    // 协议中的属性
    var wins: Int {get}      // 胜局
    var losses: Int {get}    // 败局
}

// 自定义一个协议Prizable
protocol Prizable {
    // 协议中的方法
    func isPrizable() -> Bool
}

// 自定义一个结构体BasketballRecord遵循协议Record和Prizable
struct BasketballRecord: Record, Prizable {
    // 必须有的协议属性
    var wins: Int
    var losses: Int
    // 注意:属性description是协议CustomStringConvertible中的,因为当前结构体遵循协议Record,且Record继承了CustomStringConvertible
    var description: String {
        return String(format: "WINS: %d, LOSSES: %d", [wins, losses])
    }
    
    // 必须实现协议Prizable中的方法
    func isPrizable() -> Bool {
        return wins > losses
    }
}

// 自定义一个结构体FootballRecord遵循协议Prizable和CustomStringConvertible
struct Student: Prizable, CustomStringConvertible {
    var name: String
    var score: Int
    // 必须有协议CustomStringConvertible中的属性
    var description: String {
        return name
    }
    
    // 必须实现协议Prizable中的方法
    func isPrizable() -> Bool {
        return score >= 60
    }
}

// 自定义一个函数:颁奖
// 语法:protocol<Prizable, CustomStringConvertible> 这个就是协议聚合
// 强调的是参数one必须满足同时遵循协议Prizable和CustomStringConvertible
func award(one: protocol<Prizable, CustomStringConvertible>) {
    if one.isPrizable() {
        print(one)
        print("祝贺你,你获得啦奖励!")
    } else {
        print(one)
        print("不要灰心,你没有获得奖励!")
    }
}

// 实例化
let basketballTeamRecord = BasketballRecord(wins: 2, losses: 2)
let lidong = Student(name: "Frank", score: 99)
// 调用函数
award(basketballTeamRecord)
award(lidong)

 运行结果:

  WINS: 2, LOSSES: 2

  不要灰心,你没有获得奖励!

  lidong

  祝贺你,你获得啦奖励!

7.泛型约束

// 自定义一个协议Prizable
protocol Prizable {
    // 协议中的方法
    func isPrizable() -> Bool
}

// 自定义一个结构体FootballRecord遵循协议Prizable和CustomStringConvertible
struct Student: Prizable, CustomStringConvertible, Equatable, Comparable {
    var name: String
    var score: Int
    // 必须有协议CustomStringConvertible中的属性
    var description: String {
        return name
    }
    
    // 必须实现协议Prizable中的方法
    func isPrizable() -> Bool {
        return score >= 60
    }
}
// 实现协议Equatable和Comparable的运算符重载
func == (s1: Student, s2: Student) -> Bool { return s1.score == s2.score }
func < (s1: Student, s2: Student) -> Bool { return s1.score < s2.score }

// 自定义函数:找第一名
// 语法:<T: Comparable>(seq: [T]) 这个就是泛型约束
// 强调:参数为一个泛型数组,里面的泛型元素必须遵循协议Comparable
// 返回值:返回一个泛型元素
func topOne<T: Comparable>(seq: [T]) -> T {
    // 断言:数组不能为空
    assert(seq.count > 0)
    // 结尾闭包:$0上一次比较得到的最大值,$1当前正在比较的值
    return seq.reduce(seq[0]) { max($0, $1) }
}

// 自定义一个函数:给第一名且分数在90分以上的颁奖,所以这里肯定是返回可选型
// 语法:<T: protocol<Prizable, Comparable>> 这个是泛型约束
// 强调:泛型T必须同时遵循协议Prizable和Comparable
func topPrizableOne<T: protocol<Prizable, Comparable>>(seq: [T]) -> T? {
    // 采用结尾闭包的形式
    return seq.reduce(nil) { (tempTop: T?, contender: T) in
        guard contender.isPrizable() else {
            return tempTop
        }
        guard let tempTop = tempTop else {
            return contender
        }
        return max(tempTop, contender)
    }
}

// 实例化
let s1 = Student(name: "s1", score: 99)
let s2 = Student(name: "s2", score: 99)
let s3 = Student(name: "s3", score: 87)
let s4 = Student(name: "s4", score: 50)
let students = [s1, s2, s3, s4]
// 尝试解包:如果有值,返回name;如果没有值,返回nil
topPrizableOne(students)?.name

8.委托模式

// 自定义一个会合制游戏协议TurnBasedGame
// 只要是回合制游戏必须遵守这个协议,才有属性turn和方法play()
protocol TurnBasedGame {
    // 协议中的属性
    var turn: Int {get set}  // 会合数
    
    // 协议中的方法
    func play()              // 游戏过程方法
}

// 自定义一个游戏代理协议TurnBasedGameDelegate
protocol TurnBasedGameDelegate {
    // 游戏代理必须实现以下四个代理方法
    func gameStart()         // 游戏开始
    func playerMore()        // 游戏动作
    func gameEnd()           // 游戏结束
    func gameOver() -> Bool  // 判断游戏是否结束
}

// 自定义一个回合制游戏类SinglePlayerTurnBasedGame
// 该类继承自会合制游戏协议TurnBasedGame
class SinglePlayerTurnBasedGame: TurnBasedGame {
    // 类中必须有协议的属性
    var turn: Int = 0
    
    // 代理:委托模式
    var delegate: TurnBasedGameDelegate!
    
    // 类中必须实现的协议方法
    func play() {
        // 1.游戏开始做些什么工作委托给代理
        delegate.gameStart()
        
        // 2.判断游戏是否结束
        while !delegate.gameOver() {
            // 游戏没有结束,委托给代理
            print("会合\(turn):")
            delegate.playerMore()
            turn += 1
        }
        
        // 3.游戏结束要做些什么工作委托给代理
        delegate.gameEnd()
    }
}

// 自定义一个回合制丢色子游戏类RollNumberGame
// 该类继承自回合制游戏类SinglePlayerTurnBasedGame
// 该类遵守游戏代理协议TurnBasedGameDelegate
class RollNumberGame: SinglePlayerTurnBasedGame, TurnBasedGameDelegate {
    // 自己的属性
    var score = 0
    
    // override:重写
    override init() {
        super.init()
        delegate = self
    }
    
    // 协议方法<TurnBasedGameDelegate>
    // 1.游戏开始时调用
    func gameStart() {
        score = 0
        turn = 0
        print("欢迎来到丢色子游戏.")
        print("试一试看你能多少回合丢到100分!")
    }
    // 2.游戏动作
    func playerMore() {
        let rollNumber = Int(arc4random()) % 6 + 1
        score += rollNumber
        print("你当前的色子数为:\(rollNumber); 你目前的得分是:\(score)")
    }
    // 3.游戏结束时调用
    func gameEnd() {
        print("恭喜你!你赢得了比赛,你的回合数是:", score)
    }
    // 4.判断游戏是否结束
    func gameOver() -> Bool {
        return score >= 100
    }
}

// 自定义一个回合制石头剪刀布游戏类RollNumberGame
// 该类继承自回合制游戏类SinglePlayerTurnBasedGame
// 该类遵守游戏代理协议TurnBasedGameDelegate
class RockPaperScissors: SinglePlayerTurnBasedGame, TurnBasedGameDelegate {
    // 定义一个枚举
    enum Shape: Int, CustomStringConvertible {
        case Rock
        case Scissors
        case Papper
        
        // 自定义一个方法
        // 判断是否可以打败
        func beat(shape: Shape) -> Bool {
            return (rawValue + 1) % 3 == shape.rawValue
        }
        
        // 必须有的协议CustomStringConvertible的属性
        var description: String {
            switch self {
            case .Rock:
                return "石头"
            case .Scissors:
                return "剪刀"
            case .Papper:
                return ""
            }
        }
    }
    
    // 自己的属性
    var wins = 0
    
    // override:重写
    override init() {
        super.init()
        delegate = self
    }
    
    // 自定义方法
    // 出一个手势
    static func go() -> Shape {
        return Shape(rawValue: Int(arc4random()) % 3)!
    }
    
    // 协议方法<TurnBasedGameDelegate>
    // 1.游戏开始时调用
    func gameStart() {
        wins = 0
        turn = 0
        print("欢迎来到剪刀石头布游戏.")
        print("游戏采用三局两胜制!")
    }
    // 2.游戏动作
    func playerMore() {
        let yourShape = RockPaperScissors.go()
        let otherShape = RockPaperScissors.go()
        print("你出的是:\(yourShape)")
        print("对方出的是:\(otherShape)")
        
        guard yourShape != otherShape else {
            print("平局!")
            turn -= 1     // 平局重新来
            return
        }
        
        // 判断是否击败
        if yourShape.beat(otherShape) {
            print("你赢啦这一会合!")
            wins += 1
        } else {
            print("你输啦这一会合!")
        }
    }
    // 3.游戏结束时调用
    func gameEnd() {
        if wins >= 2 {
            print("恭喜你赢啦!")
        } else {
            print("你输啦!")
        }
    }
    // 4.判断游戏是否结束
    func gameOver() -> Bool {
        return turn >= 3
    }
}

// 实例化
// 1.回合制丢色子游戏
let rollNumberGame = RollNumberGame()
rollNumberGame.play()
// 2.回合制剪刀石头布游戏
let rockPaperScissors = RockPaperScissors()
rockPaperScissors.play()

9.可选的协议方法

1)注意点

 a.协议定义前面必须用@objc修饰,这样就保证协议既可以用于OC,也可以用于Swift

 b.可选的协议方法定义前面必须使用关键字optional修饰

 c.遵守协议的必须是类,且必须是OC的类(继承自OC的某一个类或直接继承NSObject)

 d.那么在调用的时候,该协议返回的就是一个可选型,所以必须先解包,再调用

2)代码

// 协议定义前面必须用@objc修饰
@objc protocol TurnBasedGameDelegate {
    // 可选的协议方法定义前面必须使用关键字optional修饰
    optional func turnStart()
}

// 遵守协议的必须是类,且必须是OC的类(继承自OC的某一个类或直接继承NSObject)
class RockPaperScissors: NSObject, TurnBasedGameDelegate {
    
}

 

十九、错误处理

1.强制退出程序

// release版本无效
// 断言
assert(1 > 0)
assert(1 > 0, "Error")
// 程序直接中断
// assertionFailure("Error")

// release版本有效
// 断言
precondition(1 > 0)
precondition(1 > 0, "Error")
// 程序直接中断
// fatalError("Error")

2.错误处理

1)概念

a.方法实现的错误处理

 1️⃣可选型:异常时返回nil

 2️⃣抛异常:方法中添加关键字throws,并定义错误枚举,该枚举必须继承自ErrorType

b.方法调用的错误处理

 1️⃣对于可选型,采用解包

 2️⃣对于抛异常的处理主要看下面的代码

2)代码示例

// 方法实现部分的错误处理
class VendingMachine {
    
    // 定义一个结构体Item(商品)
    struct Item {
        // 定义一个枚举Type(商品类型)
        enum Type: String {
            case Water  // 矿泉水类
            case Cola   // 可乐类
            case Juice  // 橙汁类
        }
        
        let type: Type // 类型
        let price: Int // 价格
        var count: Int // 库存
    }
    
    // 定义一个私有的成员属性(所有商品)
    private var items = ["矿泉水" : Item(type: .Water, price: 2, count: 10), "可乐" : Item(type: .Cola, price: 3, count: 5), "橙汁" : Item(type: .Juice, price: 5, count: 3)]
    
    // 定义一个枚举(错误类型)
    // 枚举Error必须遵循协议ErrorType
    // 这里遵循CustomStringConvertible是为打印异常方便
    enum Error: ErrorType, CustomStringConvertible {
        case NoSuchItem             // 没有此商品
        case NotEnoughMoney(Int)    // 没有足够的钱
        case OutOfStock             // 商品库存不够
        
        // 协议CustomStringConvertible的属性
        var description: String {
            switch self {
            case .NoSuchItem:
                return "没有此商品!"
            case .NotEnoughMoney(let price):
                return "此商品价格为:\(price)元!"
            case .OutOfStock:
                return "此商品没有库存!"
            }
        }
    }
    
    // 方法
    // 1.贩卖函数
    // 由于这个函数可能存在异常,所以函数返回值必须作两种情况处理:
    // 1)存在异常,返回值前面加关键字throws
    // 2)程序正常运行,返回Int类型的值
    func vend(itemName itemName: String, money: Int) throws -> Int {
        // 1.异常一:没有此商品
        guard let item = items[itemName] else {
            // 抛出异常
            throw Error.NoSuchItem
        }
        // 2.异常二:钱不够
        guard money >= item.price else {
            // 抛出异常(带关联值)
            throw Error.NotEnoughMoney(item.price)
        }
        // 3.异常三:商品没有库存
        guard item.count > 0 else {
            // 抛出异常
            throw Error.OutOfStock
        }
        // 发货
        dispenseItem(itemName)
        return money - item.price
    }
    // 2.发货函数
    private func dispenseItem(itemName: String) {
        // 能到这一步,说明一定存在该商品,所以这里采用强制解包
        items[itemName]!.count -= 1
        print("你的东西请收好:\(itemName), 欢迎下次再来!")
    }
}

    
// 方法调用部分的错误处理
let machine = VendingMachine()
var pocketMoney = 3

// 1.强制处理(类似于可选型的强制解包)
// 这样就有一个问题,如果解包不成功就会报错
pocketMoney = try! machine.vend(itemName: "可乐", money: pocketMoney)

// 2.尝试处理(类似于可选型的尝试解包)
// 1)第一种形式(不会区分异常种类,如果异常就会返回一个nil)
try? machine.vend(itemName: "可乐", money: pocketMoney)
// 2)第二种形式(不会区分异常种类,如果异常就会返回一个nil,if是正常情况,else是异常情况)
if let pocketMoney = try? machine.vend(itemName: "可乐", money: pocketMoney) {
    // 正常
} else {
    // 异常
}

// 3.通过错误类型来分情况处理(do-catch)
do{
    pocketMoney = try machine.vend(itemName: "可乐", money: pocketMoney)
}
catch VendingMachine.Error.NoSuchItem {
    print("没有此商品!")
}
catch VendingMachine.Error.NotEnoughMoney(let price) {
    // 带关联值得错误处理
    print("此商品价格为:\(price)元!")
}
catch VendingMachine.Error.OutOfStock {
    print("此商品没有库存!")
}
catch {
    print("其他的错误处理")
}

// 4.接收错误
do{
    pocketMoney = try machine.vend(itemName: "可乐", money: pocketMoney)
}
catch let error as VendingMachine.Error {
    // 这里的as就相当与类型强转
    // 这里直接打印error,是因为Error这个枚举遵循了协议CustomStringConvertible
    print(error)
}
catch {
    print("其他的错误处理")
}

3.defer语句

1)defer语句起控制转移的作用

2)当在一个作用域范围内,有多个defer的时候,如果都会执行时,就会倒序调用

3)当在一个退出之后,程序回去寻找退出之前的defer语句,并执行它,但是之后的defer语句不会执行

defer语句保证了一些重要的语句在因为异常抛出之后还能够被执行

 

二十、内存管理

1.强引用和弱引用

1)强引用

// 定义一个类Person
class Person {
    var name: String  // 名字
    var dog: Dog?     // 宠物狗
    
    // 初始化函数
    init(name: String) {
        self.name = name
        print(name, "被初始化!")
    }
    
    // 释放函数
    deinit{
        print("Person", name, "被释放!")
    }
}

// 定义一个类Dog
class Dog {
    let name: String     // 名字
    var person: Person?  // 主人
    
    // 初始化函数
    init(name: String) {
        self.name = name
        print(name, "被初始化!")
    }
    
    // 释放函数
    deinit{
        print("Dog", name, "被释放!")
    }
}

// 初始化
var person: Person? = Person(name: "Frank")
var dog: Dog? = Dog(name: "dahuang")
// 相互强引用
person?.dog = dog
dog?.person = person
// 相互强引用造成尽管两个对象都为nil,也不会被释放
person = nil
dog = nil

 运行结果:

   Frank 被初始化!

  dahuang 被初始化!

 问题是没有释放掉内存空间,造成内存泄露问题,分析如下图:

2)weak弱引用

a.weak弱引用需要注意两点:

 1️⃣weak弱引用的变量必须是var,且只能修饰类引用

 2️⃣weak弱引用的变量必须是可选型

b.代码示例

// 定义一个类Person
class Person {
    var name: String  // 名字
    var dog: Dog?     // 宠物狗
    
    // 初始化函数
    init(name: String) {
        self.name = name
        print(name, "被初始化!")
    }
    
    // 释放函数
    deinit{
        print("Person", name, "被释放!")
    }
}

// 定义一个类Dog
class Dog {
    let name: String          // 名字
    // weak:打破相互强引用
    // 注意:1.weak弱引用的变量必须是var 2.weak弱引用的变量必须是可选型
    weak var person: Person?  // 主人
    
    // 初始化函数
    init(name: String) {
        self.name = name
        print(name, "被初始化!")
    }
    
    // 释放函数
    deinit{
        print("Dog", name, "被释放!")
    }
}

// 初始化
var person: Person? = Person(name: "Frank")
var dog: Dog? = Dog(name: "dahuang")
// weak打破相互强引用
person?.dog = dog
dog?.person = person
// weak打破相互强引用,两个对象被释放
person = nil
dog = nil

 运行结果:

  Frank 被初始化!

  dahuang 被初始化!

  Person Frank 被释放!

  Dog dahuang 被释放!

 弱引用内存空间被安全释放,原因分析如下图:

3)unowned弱引用(效果同weak)

a.unowned弱引用需要注意两点:

 1️⃣unowned修饰的变量不能是可选型,且只能修饰类引用

 2️⃣unowned修饰的变量所指向的内存空间不可以提前被释放,这也是它的一个不安全的地方

b.代码示例

// 定义一个类Person
class Person {
    var name: String             // 名字
    var creditCard: CreditCard?  // 信用卡(不一定有,所以是可选型)
    
    // 初始化函数
    init(name: String) {
        self.name = name
        print(name, "被初始化!")
    }
    
    // 释放函数
    deinit{
        print("Person", name, "被释放!")
    }
}

// 定义一个类CreditCard
class CreditCard {
    let number: String            // 卡号
    // unowned:打破相互强引用,效果同weak
    // unowned没有weak的限定条件,但是unowned修饰的变量不能是可选型
    // unowned修饰的变量所指向的空间不可以提前被释放,这个也是它的一个不安全的地方
    unowned let customer: Person  // 卡主(每一张信用卡必须有一个卡主)
    
    // 初始化函数
    init(number: String, customer: Person) {
        self.number = number
        self.customer = customer
        print("CreditCard", number, "被初始化!")
    }
    
    // 释放函数
    deinit{
        print("CreditCard", number, "被释放!")
    }
}

// 初始化
var person: Person? = Person(name: "Frank")
var creditCard: CreditCard? = CreditCard(number: "123456789", customer: person!)
// unowned打破相互强引用
person?.creditCard = creditCard
// unowned打破相互强引用,对象被释放
creditCard = nil
person = nil
// 注意:如果事先让person=nil,那么creditCard?.customer指向的空间被释放掉,由于unowned修饰的customer不能是可选型,就不能被系统设为nil,所以报错
// 这里就要求unowned修饰的变量所指向的内存空间不可以被提前释放

 运行结果:

  Frank 被初始化!

  CreditCard 123456789 被初始化!

  Person Frank 被释放!

  CreditCard 123456789 被释放!

4)构造函数中的强引用

// 定义一个类Country
class Country {
    let countryName: String   // 国家名
    // 隐式可选型:可以在初始化的时候暂时不去赋值
    var capital: City!        // 首都

    // 初始化函数
    init(countryName: String, capitalName: String) {
        self.countryName = countryName
        // Swift要求在初始化该类的所有不为nil的变量之前,不得提前使用该类
        // 这里的self就是在capital还没有初始化完成就被使用
        // 这样就会产生矛盾,但是如果将capital声明为隐式可选型,就OK啦
        self.capital = City(cityName: capitalName, country: self)
        print("Country", countryName, "被初始化!")
    }
    
    // 释放函数
    deinit{
        print("Country", countryName, "被释放!")
    }
}

// 定义一个类City
class City {
    let cityName: String           // 城市名
    unowned let country: Country   // 所属国家
    
    // 初始化函数
    init(cityName: String, country: Country) {
        self.cityName = cityName
        self.country = country
        print("City", cityName, "被初始化!")
    }
    
    // 释放函数
    deinit{
        print("City", cityName, "被释放!")
    }
}

// 初始化
var china: Country? = Country(countryName: "China", capitalName: "Beijing")
china = nil

2.闭包中的强引用循环

// 定义一个SmartAirConditioner类
class SmartAirConditioner {
    var temperature: Int = 26     // 空调的初始温度
    // 类对闭包的引用
    var temperatureChange: ((Int) -> ())!
    
    // 初始化函数
    init(){
        // 闭包对类的引用
        // 捕获列表
        // [unowned self]  这里必须保证self不能为nil
        // [weak self]     但是要注意self就变成了可选类型,所以必须解包
        // 使用unowned或者使用weak,具体问题具体分析,weak修饰的的是可选型,但是unowned修饰的是不为nil
        temperatureChange = { [weak self] newTemperature in
            if let weakSelf = self {
                if abs(newTemperature - weakSelf.temperature) >= 10 {
                    print("It's not healthy to do it!")
                } else {
                    weakSelf.temperature = newTemperature
                    print("New temperature \(weakSelf.temperature) is set!")
                }
            }
        }
    }
    
    // 销毁函数
    deinit{
        print("被销毁!")
    }
}

// 初始化调用
var airCon: SmartAirConditioner? = SmartAirConditioner()
airCon?.temperature  // 26
airCon?.temperatureChange(100)
airCon?.temperatureChange(24)
airCon = nil

 

二十一、类型检查和类型转换

1.类型检查

// 定义一个类MediaItem
class MediaItem {
    var name: String  // 名字
    init(name: String) {
        self.name = name
    }
}

// 定义一个类Movie继承自MediaItem
class Movie: MediaItem {
    var genre: String   // 类型
    init(name: String, genre: String) {
        self.genre = genre
        super.init(name: name)
    }
}

// 定义一个类Music继承自MediaItem
class Music: MediaItem {
    var artist: String  // 演唱者
    init(name: String, artistName: String) {
        self.artist = artistName
        super.init(name: name)
    }
}

// 定义一个类Game继承自MediaItem
class Game: MediaItem {
    var developer: String  // 开发者
    init(name: String, developer: String) {
        self.developer = developer
        super.init(name: name)
    }
}

// 初始化调用
let library: [MediaItem] = [
    Movie(name: "Zootopia", genre: "Animation"),
    Music(name: "Hello", artistName: "Adele"),
    Game(name: "LIMBO", developer: "Frank"),
    Movie(name: "功夫", genre: "动作"),
    Music(name: "红蜻蜓", artistName: "TFBoys"),
    Game(name: "贪吃蛇大战", developer: "Frank"),
    Movie(name: "战狼", genre: "战争")
]

// 循环数组:找出Movie/Music/Game的数量
var movieCount = 0
var musicCount = 0
var gameCount = 0
for mediaItem in library {
    // 判断mediaItem是否是Movie类型
    if mediaItem is Movie {
        movieCount += 1
    }
    // 判断mediaItem是否是Music类型
    else if mediaItem is Music {
        musicCount += 1
    }
    // 判断mediaItem是否是Game类型
    else if mediaItem is Game {
        gameCount += 1
    }
}

2.类型转化

// 定义一个类MediaItem
class MediaItem {
    var name: String  // 名字
    init(name: String) {
        self.name = name
    }
}

// 定义一个类Movie继承自MediaItem
class Movie: MediaItem {
    var genre: String   // 类型
    init(name: String, genre: String) {
        self.genre = genre
        super.init(name: name)
    }
}

// 定义一个类Music继承自MediaItem
class Music: MediaItem {
    var artist: String  // 演唱者
    init(name: String, artistName: String) {
        self.artist = artistName
        super.init(name: name)
    }
}

// 定义一个类Game继承自MediaItem
class Game: MediaItem {
    var developer: String  // 开发者
    init(name: String, developer: String) {
        self.developer = developer
        super.init(name: name)
    }
}

// 初始化调用
let library: [MediaItem] = [
    Movie(name: "Zootopia", genre: "Animation"),
    Music(name: "Hello", artistName: "Adele"),
    Game(name: "LIMBO", developer: "Frank"),
    Movie(name: "功夫", genre: "动作"),
    Music(name: "红蜻蜓", artistName: "TFBoys"),
    Game(name: "贪吃蛇大战", developer: "Frank"),
    Movie(name: "战狼", genre: "战争")
]
    
// 强制转换
// let item0 = library[0] as! Movie
// 尝试转换
// let item1 = library[1] as? Music

// 循环数组
for mediaItem in library {
    // 将mediaItem尝试转换为Movie,再解包
    if let movie = mediaItem as? Movie {
        print("Movie:", movie.name, "Genre:", movie.genre)
    }
    // 将mediaItem尝试转换为Music,再解包
    else if let music = mediaItem as? Music {
        print("Music:", music.name, "Artist:", music.artist)
    }
    // 将mediaItem尝试转换为Game,再解包
    else if let game = mediaItem as? Game {
        print("Game:", game.name, "Developer:", game.developer)
    }
}

3.检查协议遵守

// 定义一个协议Shape
protocol Shape {
    var name: String{get}
}

// 定义一个协议HasArea
protocol HasArea {
    func area() -> Double
}

// 定义一个结构体Point遵循协议Shape
struct Point: Shape {
    let name: String = "point"
    var x: Double
    var y: Double
}

// 定义一个结构体Rectangle遵循协议Shape和HasArea
struct Rectangle: Shape, HasArea {
    let name: String = "rectangle"
    var origin: Point
    var width: Double
    var height: Double
    
    func area() -> Double {
        return width * height
    }
}

// 定义一个结构体Circle遵循协议Shape和HasArea
struct Circle: Shape, HasArea {
    let name = "circle"
    var center: Point
    var radius: Double
    
    func area() -> Double {
        return M_PI * radius * radius
    }
}

// 定义一个结构体Line遵循协议Shape
struct Line: Shape {
    let name = "line"
    var a: Point
    var b: Point
}

// 初始化调用
let shapes: [Shape] = [
    Rectangle(origin: Point(x: 0.0, y: 0.0), width: 3.0, height: 4.0),
    Point(x: 0.0, y: 0.0),
    Circle(center: Point(x: 0.0, y: 0.0), radius: 1.0),
    Line(a: Point(x: 1.0, y: 1.0), b: Point(x: 5.0, y: 5.0))
]
    
// 循环数组:判断哪些形状有面积这个方法
for shape in shapes {
    // 判断shape是否遵循协议HasArea
    if shape is HasArea {
        print("\(shape.name) has area.")
    }
    else {
        print("\(shape.name) has no area.")
    }
}
    
// 循环数组:打印面积
for shape in shapes {
    // 尝试转化
    if let areaShape = shape as? HasArea {
        print("The area of \(shape.name) is \(areaShape.area()).")
    }
    else {
        print("\(shape.name) has no area.")
    }
}

4.NSObject、AnyObject、Any

NSObject:OC中的基类

AnyObject:相当于OC中的id类型,表示任意的对象类型

Any:任意类型,范围比AnyObject还要广,不仅可以代表对象,也可以代表函数

注:更多有关Swift的内容请关注网站:https://swift.org

posted @ 2016-06-24 14:41  Frank9098  阅读(268)  评论(0)    收藏  举报