万字长文对Swift语法一网打尽

swift所有的语法

blog.csdn.net/java_androi…

一) 基本数据类型 :

if let temp1 = obj1, let temp2 = obj2, temp1 < temp2 {
    // 当全部不为 nil 且满足子句 temp1 < temp2
    print(temp1, temp2) // 1 2
} else {
    print("obj1 or obj2 may be nil")
}

// 在数值前可加 0 进行位数填充,可加入下划线,增加可读性

var number1 = 001.234 // 1.234
var number2 = 1_234 // 1234
// 打印多个值使用逗号分隔
print(number1, number2) // 1.234 1234 

二) Swift 字符串类型及常用方法

“let greeting = "Hello, world!"
let index = greeting.firstIndex(of: ",") ?? greeting.endIndex
let beginning = greeting[..<index]
// beginning 的值为 "Hello"

// 把结果转化为 String 以便长期存储。
let newString = String(beginning)”

字符串截取:

   let index1:String.Index = stre.index(before: stre.endIndex)
        let index2:String.Index = stre.index(stre.endIndex, offsetBy: -2)
        let str22 =  stre[index2...index1]

获取某个字符: var afterChar = indexStr[indexStr.index(after: startIndex)] // e

字符串的常用方法 blog.csdn.net/java_androi…

range的范围 startIndex...indexStr.index(startIndex, offsetBy: 13)

// 字符串判空
var emptyStr = ""
if emptyStr.isEmpty {
    print("string is empty")
}
if emptyStr.count == 0 {
    print("string count is 0")
}

// 字符串判大小,会逐个比较字符大小
let str1 = "100a", str2 = "101a"
if (str1 < str2) {
    print("str1 < str2")
}
// 字符串判相等,会比较所有字符的位置都相等,才为相等的字符串
if (str1 == str2) {
    print("str1 < str2")
}

// 使用下标访问字符
var indexStr = "Hello, William"
// 获取起始下标
var startIndex: String.Index = indexStr.startIndex
var endIndex: String.Index = indexStr.endIndex
// 获取某个下标后一个下标对应的字符
var afterChar = indexStr[indexStr.index(after: startIndex)] // e
// 获取某个下标前一个下标对应的字符
var beforeChar = indexStr[indexStr.index(before: endIndex)] // m

// ... 运算符指定范围,从 startIndex 向后移动4位截取子串
var subStr = indexStr[startIndex...indexStr.index(startIndex, offsetBy: 4)] // hello
// 从endIndex 向前移动7位截取子串
var subStr2 = indexStr[indexStr.index(endIndex, offsetBy: -7)..<endIndex] // William

// 获取范围
var range = indexStr.range(of: "Hello")
// 追加字符串
indexStr.append(Character("."))
indexStr.append(" append string") // Hello, William. append string

// 插入单个字符到指定位置 Hello, William.# append string
indexStr.insert("#", at: indexStr.index(startIndex, offsetBy: 15))

// 插入一组字符 Hello, William.-#-# append string
indexStr.insert(contentsOf: ["-", "#", "-"], at: indexStr.index(startIndex, offsetBy: 15))

// 替换指定范围的字符串 How are you.-#-# append string
indexStr.replaceSubrange(startIndex...indexStr.index(startIndex, offsetBy: 13), with: "How are you")

// 删除指定位置的单个字符 How are you.-#-# append strin
indexStr.remove(at: indexStr.index(before: indexStr.endIndex))

// 删除指定范围 -#-# append strin
indexStr.removeSubrange(indexStr.startIndex...indexStr.index(indexStr.startIndex, offsetBy: 11))

// 删除所有字符 ""
indexStr.removeAll()

// 转换大小写
var uppercase = "hello, swift".uppercased() // HELLO, SWIFT
var lowercase = "HELLO, SWIFT".lowercased() // hello, swift

// 检查前后缀
var hasPrefix = uppercase.hasPrefix("he") // false
var hasSuffix = lowercase.hasSuffix("ft") // true

三) 数组 及常用方法

  1. 创建数组
// 创建整型数组
var array1: [Int] = [] // []
var arrya2: Array<Int> = [1, 2, 3] // [1, 2, 3]
var arryaInt = [1, 2, 3] // [1, 2, 3]
var array3 = Array(arrayLiteral: 1, 2, 3) // [1, 2, 3]
  1. 快捷创建重复元素的数组
var array4 = Array(repeating: "swift", count: 3) // ["swift", "swift", "swift"]
var array5 = Array(repeating: 1001, count: 3) // [1001, 1001, 1001]

  1. 数组相加
// 2个相同类型的数组相加
var array6 = [1, 2, 3] + [4, 5, 6] // [1, 2, 3, 4, 5, 6]

  1. 常用方法
// 当数组声明为可变时,才能使用增,删,改等方法,常量数组不能进行修改相关操作
var array = [1, 2, 3, 4, 5, 6, 7, 8]
print(array.count) // 8

// 判断数组是空数组
if array.isEmpty {
    print("array is empty")
} else {
    print("array is not empty")
}

// 通过下标访问元素
var ele = array[1] // 2

// 截取新数组
var subArray = array[1...2] // [2, 3]

// 获取第一个元素
var firstEle = array.first // 1

// 获取最后一个元素
var lastEle = array.last // 8

// 修改下标对应的元素
array[1] = 22
array // [1, 22, 3, 4, 5, 6, 7, 8]

// 修改指定范围的元素
array[0...2] = [1, 2, 3] // [1, 2, 3]
 
// 追加单个元素
array.append(9) // [1, 2, 3, 4, 5, 6, 7, 8, 9]

// 追加一组元素
array.append(contentsOf: [10, 11, 12]) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

// 在指定位置插入单个元素
array.insert(0, at: 0) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

// 在指定位置插入一组元素
array.insert(contentsOf: [-3, -2, -1], at: 0) // [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

// 移除指定元素
array.remove(at: 1) // -2

// 移除一组元素
array.removeSubrange(0...2) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

// 移除首个元素
array.removeFirst() // 1

// 移除末尾元素
array.removeLast() // 12

// 移除前几个元素
array.removeFirst(3) // [5, 6, 7, 8, 9, 10, 11]

// 移除后几个元素
array.removeLast(3) // [5, 6, 7, 8]

// 替换指定范围的元素
array.replaceSubrange(0...3, with: [1, 2, 3, 4]) // [1, 2, 3, 4]

// 判断包含指定元素
if array.contains(3) {
    print("array contains 3")
}

// 移除所有元素
array.removeAll() // []

var sortArr = [2, 1, 3, -1]

// 从小到大排序
sortArr.sorted(by: <) // [-1, 1, 2, 3]

// 从大到小排序
sortArr.sorted(by: >) // [3, 2, 1, -1]

// 获取数组最大值
sortArr.min() // -1

// 获取数组最小值
sortArr.max() // 3

定义二维数组 matrix:[[Int]], matrix[row][column]

    var visited = Array(repeating: Array(repeating: false, count: matrix[0].count), count: matrix.count)

        matrix[row][col] == path[path.index(path.startIndex, offsetBy: pathIndex)] && !visited[row][col]) {

数组的常用方法

创建 var array4 = Array(repeating: "swift", count: 3) // ["swift", "swift", "swift"] 创建

var array1: [Int] = [] // []
var arrya2: Array<Int> = [1, 2, 3] // [1, 2, 3]
var arryaInt = [1, 2, 3] // [1, 2, 3]

数组相加 + 数组为空isEmpty 数组截取 var subArray = array[1...2] // [2, 3] 数组的元素:

// 获取第一个元素
var firstEle = array.first // 1

// 获取最后一个元素
var lastEle = array.last // 8

常用方法

// 当数组声明为可变时,才能使用增,删,改等方法,常量数组不能进行修改相关操作
var array = [1, 2, 3, 4, 5, 6, 7, 8]
print(array.count) // 8

// 判断数组是空数组
if array.isEmpty {
    print("array is empty")
} else {
    print("array is not empty")
}

// 通过下标访问元素
var ele = array[1] // 2

// 截取新数组
var subArray = array[1...2] // [2, 3]

// 获取第一个元素
var firstEle = array.first // 1

// 获取最后一个元素
var lastEle = array.last // 8

// 修改下标对应的元素
array[1] = 22
array // [1, 22, 3, 4, 5, 6, 7, 8]

// 修改指定范围的元素
array[0...2] = [1, 2, 3] // [1, 2, 3]
 
// 追加单个元素
array.append(9) // [1, 2, 3, 4, 5, 6, 7, 8, 9]

// 追加一组元素
array.append(contentsOf: [10, 11, 12]) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

// 在指定位置插入单个元素
array.insert(0, at: 0) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

// 在指定位置插入一组元素
array.insert(contentsOf: [-3, -2, -1], at: 0) // [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

// 移除指定元素
array.remove(at: 1) // -2

// 移除一组元素
array.removeSubrange(0...2) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

// 移除首个元素
array.removeFirst() // 1

// 移除末尾元素
array.removeLast() // 12

// 移除前几个元素
array.removeFirst(3) // [5, 6, 7, 8, 9, 10, 11]

// 移除后几个元素
array.removeLast(3) // [5, 6, 7, 8]

// 替换指定范围的元素
array.replaceSubrange(0...3, with: [1, 2, 3, 4]) // [1, 2, 3, 4]

// 判断包含指定元素
if array.contains(3) {
    print("array contains 3")
}

// 移除所有元素
array.removeAll() // []

var sortArr = [2, 1, 3, -1]

// 从小到大排序
sortArr.sorted(by: <) // [-1, 1, 2, 3]

// 从大到小排序
sortArr.sorted(by: >) // [3, 2, 1, -1]

// 获取数组最大值
sortArr.min() // -1

// 获取数组最小值
sortArr.max() // 3


数组的遍历

let arr = [11, 22, 33]

for item in arr {
    print(item)
}

// 打印数组的下标及对应元素
for item in arr.enumerated() {
    print(item) // (offset: 0, element: 11) (offset: 1, element: 22) (offset: 2, element: 33)
}

// 下标遍历
for index in arr.indices {
    print(arr[index])
}

四 Swift 集合 Set 及常用方法

  1. 创建Set集合
// 创建Set
var set: Set<Int> = [1, 2, 3]
var set2 = Set(arrayLiteral: 1, 2, 3)
  1. 获取元素
// set 获取最小值
set.min()

// 获取第一个元素,顺序不定
set[set.startIndex]
set.first

// 通过下标获取元素,只能向后移动,不能向前
// 获取第二个元素
set[set.index(after: set.startIndex)]

// 获取某个下标后几个元素
set[set.index(set.startIndex, offsetBy: 2)]
  1. 常用方法

// 获取元素个数
set.count

// 判断空集合
if set.isEmpty {
   print("set is empty")
}

// 判断集合是否包含某个元素
if (set.contains(3)) {
    print("set contains 3")
}

// 插入
set.insert(0)

// 移除
set.remove(2)
set.removeFirst()

// 移除指定位置的元素,需要用 ! 拆包,拿到的是 Optional 类型,如果移除不存在的元素,EXC_BAD_INSTRUCTION
set.remove(at: set.firstIndex(of: 1)!)

set.removeAll()


var setStr1: Set<String> = ["1", "2", "3", "4"]
var setStr2: Set<String> = ["1", "2", "5", "6"]

// Set 取交集
setStr1.intersection(setStr2) // {"2", "1"}

// Set 取交集的补集
setStr1.symmetricDifference(setStr2) // {"4", "5", "3", "6"}

// Set 取并集
setStr1.union(setStr2) // {"2", "3", "1", "4", "6", "5"}

// Set 取相对补集(差集),A.subtract(B),即取元素属于 A,但不属于 B 的元素集合
setStr1.subtract(setStr2) // {"3", "4"}

var eqSet1: Set<Int> = [1, 2, 3]
var eqSet2: Set<Int> = [3, 1, 2]

// 判断 Set 集合相等
if eqSet1 == eqSet2 {
    print("集合中所有元素相等时,两个集合才相等,与元素的顺序无关")
}

let set3: Set = [0, 1]
let set4: Set = [0, 1, 2]

// 判断子集
set3.isSubset(of: set4) // set3 是 set4 的子集,true
set3.isStrictSubset(of: set4) // set3 是 set4 的真子集,true

// 判断超集
set4.isSuperset(of: set3) // set4 是 set3 的超集,true
set4.isStrictSuperset(of: set3) // set4 是 set3 的真超集,true

  1. Set 遍历

// 遍历元素
for ele in set4 {
    print(ele)
}

// 遍历集合的枚举
for ele in set4.enumerated() {
    print(ele)
}

// 下标遍历
for index in set4.indices {
    print(set4[index])
}

// 从小到大排序后再遍历
for ele in set4.sorted(by: <) {
    print(ele)
}


四) Swift 字典 Dictionary 集合类型

##1) 创建空字典

var emptyDict1: [Int : String] = [:]
var emptyDict2: Dictionary<Int, String> = Dictionary()

// 指定键值类型

var dict: [Int : String] = [0: "Zero", 1: "One", 2: "Two"]
var dict2: Dictionary<Int, String> = Dictionary(dictionaryLiteral: (1, "One"), (2, "Two"))

// 自动推断键值类型
var dictAuto = [0: "Zero", 1: "One", 2: "Two"]


var dic1=[1:1,2:12,3:32,4:16,5:15]
var dic2:Dictionary<String,String>=[:]
var dic3=Dictionary<String,String>()
var dic4=[String : String]()
  1. 获取元素
// 获取元素个数
dict.count // 3

// 判断字典为空
dict.isEmpty // false

// 获取键对应的值
dict[1] // One
  1. 更新键值对

// 修改键对应的值
dict[0] = "000" // 000

// 如果键存在,就更新,否则就新增键值对
dict[-1] = "-1" // -1

// 更新键对应的值,如果键不存在,返回 nil
if let lastValue = dict.updateValue("new one", forKey: 1) {
    print("the last value is \(lastValue)") // the last value is One
}

// 移除键值对
dict.removeValue(forKey: -1) // -1

// 移除所有键值对
//dict.removeAll()
  1. 遍历字典

// dict [1: "new one", 2: "Two", 0: "000"]

// 遍历字典的键
for ele in dict.keys {
    print(ele)
}

// 遍历字典的值
for ele in dict.values {
    print(ele)
}

// 元组遍历,直接获取键值对
for (key, val) in dict {
    print("\(key):\(val)")
}

// 对 key 进行从小到大排序后遍历,并对值进行拆包
for ele in dict.keys.sorted(by: <) {
    print(dict[ele]!)
}

//1 读取字典元素
var test1Dic=["key1":"你好","key2":"Swift","key3":"正在学习","key4":"字典","key5":"取值",]
 
var test1Str=test1Dic["key2"]
println("\(test1Str)")
 
//此处取字典中未定义的键 不会报错,取出来的值为nil
var test1Str2=test1Dic["key"]
println("\(test1Str2)")
 
 
 
//2 获取字典元素的个数
 
println(test1Dic.count)
 
//3 增加字典的元素
 
test1Dic["key"]="test"
println(test1Dic)
 
//4 删除字典中的元素
 
test1Dic.removeValueForKey("key1")
println(test1Dic)
 
//5 修改字典中的元素
 
// 5.1 直接修改
test1Dic["key"]="testkey"
 
// 5.2 使用 updateValue
var oldStr=test1Dic.updateValue("testkeytest", forKey: "key")
println(oldStr)
println(test1Dic)
 
//6 遍历
//6.1遍历所有的键值对
 
for (key,value) in test1Dic{
    println("key:\(key) value:\(value)")
}
 
//6.2 遍历所有的键
for test6Str in test1Dic.keys{
    println(test6Str)
}
 
//6.2 遍历所有的值
for test6Str2 in test1Dic.values{
    println(test6Str2)
}
 
//7 字典转数组
//7.1 将所有的键转为数组
var test7Keys=Array(test1Dic.keys)
println(test7Keys)
 
//7.1 将所有的值转为数组
var test7Values=Array(test1Dic.values)
println(test7Values)


swift 字典常用方法

https://blog.csdn.net/qq_32582087/article/details/49737755

https://wenku.baidu.com/view/2eef2223874769eae009581b6bd97f192279bf3a.html

五) 集合 set 常用方法

** 集合的常用**

https://wenku.baidu.com/view/92af6034f9d6195f312b3169a45177232f60e4d0.html

import UIKit

var greeting = "Hello, playground"

// 集合类型:集合 Set
// 不关注顺序,但不可以重复

// 创建Set
var set: Set<Int> = [1, 2, 3]
var set2 = Set(arrayLiteral: 1, 2, 3)

// set 获取最大值
set.max()

// set 获取最小值
set.min()

// 获取第一个元素,顺序不定
set[set.startIndex]
set.first

// 通过下标获取元素,只能向后移动,不能向前
// 获取第二个元素
set[set.index(after: set.startIndex)]
// 获取某个下标后几个元素
set[set.index(set.startIndex, offsetBy: 2)]

// 获取元素个数
set.count

// 判断空集合
if set.isEmpty {
   print("set is empty")
}

// 判断集合是否包含某个元素
if (set.contains(3)) {
    print("set contains 3")
}

// 插入
set.insert(0)

// 移除
set.remove(2)
set.removeFirst()
// 移除指定位置的元素,需要用 ! 拆包,拿到的是 Optional 类型,如果移除不存在的元素,EXC_BAD_INSTRUCTION
set.remove(at: set.firstIndex(of: 1)!)

set.removeAll()


var setStr1: Set<String> = ["1", "2", "3", "4"]
var setStr2: Set<String> = ["1", "2", "5", "6"]

// Set 取交集
setStr1.intersection(setStr2) // {"2", "1"}

// Set 取交集的补集
setStr1.symmetricDifference(setStr2) // {"4", "5", "3", "6"}

// Set 取并集
setStr1.union(setStr2) // {"2", "3", "1", "4", "6", "5"}

// Set 取相对补集(差集),A.subtract(B),即取元素属于 A,但不属于 B 的元素集合
setStr1.subtract(setStr2) // {"3", "4"}

var eqSet1: Set<Int> = [1, 2, 3]
var eqSet2: Set<Int> = [3, 1, 2]
// 判断 Set 集合相等
if eqSet1 == eqSet2 {
    print("集合中所有元素相等时,两个集合才相等,与元素的顺序无关")
}

let set3: Set = [0, 1]
let set4: Set = [0, 1, 2]
// 判断子集
set3.isSubset(of: set4) // set3 是 set4 的子集,true
set3.isStrictSubset(of: set4) // set3 是 set4 的真子集,true
// 判断超集
set4.isSuperset(of: set3) // set4 是 set3 的超集,true
set4.isStrictSuperset(of: set3) // set4 是 set3 的真超集,true

// 遍历元素
for ele in set4 {
    print(ele)
}

// 遍历集合的枚举
for ele in set4.enumerated() {
    print(ele)
}

// 下标遍历
for index in set4.indices {
    print(set4[index])
}

// 从小到大排序后再遍历
for ele in set4.sorted(by: <) {
    print(ele)
}

六) Swift 运算符、循环、流程控制 for-in, while, if-else, switch-case, guard-else

  1. 运算符:三目,空合并,区间运算符

// 元组比较大小。需要元素个数一致,对应的位置的元素类型相同,每一个元素都必须支持比较运算操作。
// 从第一个元素开始比较,如果没有比较出结果,那么继续依次比较,直到比出结果为止。
var a = (1, 2, "3")
var b = (1, 2, "4")

var c = a < b // true

// 条件判断
if a > b {
    print("a > b")
} else {
    print("a < b")
}

// 三目运算,同 java
print(a > b ? "a > b" : "a < b") // a < b

var str: String? = "text"
var result: String = str != nil ? str! : ""

// 空合并运算符,如果 str 为 nil,则赋值空串,同 kotlin 的 Elvis 运算符 ?:
result = str ?? ""

// 区间运算符,[1, 5],范围是 >=1, <=5,类似于 Kotlin 的区间 1..5
var rang1 = 1...5

// 区间运算符,[1, 5),范围是 >=1, <5
var rang2 = 1..<5

// 判断是否在某个区间范围内
print(rang1 ~= 2) // true
  1. 循环:for-in, while, repeat-while

// 区间运算符用于循环
for index in rang1 {
    print(index)
}

// 中断循环 break
for index in 0...2 {
    if index == 1 {
        break
    }
    print(index) // 0
}

// 跳过当前循环 continue
for index in 0...2 {
    if index == 1 {
        continue
    }
    print(index) // 0 2
}

// 中断外层循环 break,类似于 Kotlin 的限定符
OutterLabel:for outterIndex in 0...2 {
    for index in 0...1 {
        if outterIndex == 1 {
            break OutterLabel
        }
        print(index) // 0 1
    }
}

// while 循环
var i = 0
while i < 3 {
    print(i) // 0, 1, 2
    i += 1
}

// 即 do-while,先执行一次循环,再判断条件
var k = 0
repeat {
    print(k) // 0, 1, 2
    k += 1
} while k < 3


  1. 流程控制:if-else, switch-case

// 流程控制 if-else
let number = 18
if number < 15 {
    print("number < 15")
} else if number >= 16 && number <= 20 {
    print("number >= 16 && number <= 20")
} else {
    print("number > 20")
}

// 流程控制 switch-case
var caseStr = "a"
switch caseStr {
case "a":
    print("value is a")
case "b":
    print("value is b")
default:
    print("default value")
}

// switch 子句多条件匹配
switch caseStr {
case "a","b","c":
    print("match success")
default:
    print("default value")
}

// switch 子句区间范围匹配
let age = 18
switch age {
case 16..<18:
    print("match age 16...18")
case 18...20:
    print("match age 18..<20")
default:
    print("default value")
}

// switch 元组匹配
let intTuple = (1, 2)
switch intTuple {
case (1, 2):
    print("match success 1, 2")
    fallthrough // 继续执行后续 case 匹配,不跳出 switch
case (2, 2):
    print("match success 2, 2")
    fallthrough
case (_, 2):
    // 选择性匹配,第一个匿名不关注,只有第二个能匹配,就算匹配成功
    print("match success _,2")
    fallthrough
case (0...2, 0...2):
    // 匹配元组元素的范围
    print("match range success")
case (let a, 1):
    print("捕获元素: \(a)")
case let(a, b) where a < b:
    // 同 (let a, let b),增加 where 字句判断
    print("捕获元组: \(a),\(b)")
    fallthrough
default:
    print("default")
}

  1. guard-else 守护判断
// 调用 method
method()

func method() {
    // 守护语句,当 guard 后面的条件成立时,才继续执行,替换之前的 if-return
    guard number > 20 else {
        return
    }
    print("continue execute")
}

七) Swift 函数与闭包

  1. 创建函数

// 创建函数,无参,无返回值,同 func func1() -> Void
func func1() {
    print("no params func")
}

func1()

// 创建函数,带参,带返回类型
func func2(param: Int) -> Bool {
    return param > 60
}

func2(param: 80)



// 创建函数,带多个参数,返回类型为元组
func func3(param1: String, param2: Int) -> (result1: String, result2: Int) {
    return (param1 + "11", param2 + 20)
}

let tuple = func3(param1: "param", param2: 60)
if tuple.result1.starts(with: "param") {
    print(tuple.result2)
}

// 创建函数,带参,返回类型为 Optional
func func4(param: Int) -> Int? {
    guard param > 2 else {
        return nil
    }
    return param + 2
}

if let result = func4(param: 3) {
    print(result)
}

  1. 函数内外参数命名

// 函数参数指定外部名称
func outerNameFunc(name1 param1: Int, name2 param2: Int, param3: Int) {
    print(param1, param2, param3)
}
// 函数参数使用外部名称
outerNameFunc(name1: 1, name2: 2, param3: 3)

func normalFunc(param1: Int, param2: Int, param3: Int) {
    print(param1, param2, param3)
}
// 默认函数参数的内部名称和外部名称一致,调用函数时需要指定参数名称
normalFunc(param1: 1, param2: 2, param3: 3)

// 调用函数时省略参数名称
func annoFunc(_ param1: Int, _ param2: Int, _ param3: Int) {
    print(param1, param2, param3)
}
annoFunc(1, 2, 3)


  1. 函数参数指定默认值

func func5(param1: Int, param2: Int = 2, param3: Int = 3) {
    print(param1, param2, param3)
}
// 调用时,可以只传入没有默认值的参数
func5(param1: 1)
// 调用时,参数位置要严格对应
func5(param1: 1, param2: 22)


// 函数参数指定默认值
func func6(param1: Int, param2: Int = 2, param3: Int) {
    print(param1, param2, param3)
}
// 调用时,参数位置要严格对应
func6(param1: 1, param3: 33)


  1. 可变参数

// 函数传入多个可变数量的参数,类似于 Kotlin 的 vararg
func mutableParamFunc(param: Int...) {
    var sum = 0
    for ele in param {
        sum += ele
    }
    print(sum)
}
mutableParamFunc(param: 1, 2)
mutableParamFunc(param: 1, 2, 3, 4)

// swift 的函数参数值(除引用类型外)默认是不可修改的
func immutableParam(param: Int) {
//    param += 1 // 编译失败 error: left side of mutating operator isn't mutable: 'param' is a 'let' constant
}

// 为了可以在函数参数内部修改参数值,可以使用 inout 修饰参数
func immutableParam(param:inout Int) {
    param += 1 // 编译通过
    print(param) // 2
}
// 调用时需要使用 & 符号
var number = 1
immutableParam(param: &number)
print(number) // 2, number的值也被修改了
  1. 函数类型引用,函数嵌套

// 函数可以作为类型进行声明,就像使用其他类型一样
let func7Name: (Int, Int) -> Bool
// 将闭包赋值给函数变量
func7Name = {(param1: Int, param2: Int) in
    return param1 > param2
}
// 调用函数变量
func7Name(1, 2) // false

// 函数作为参数传入
func func8(funParam: (Int, Int) -> Bool) {
    print(funParam(2, 1)) // true
}
// 将 func7 传入 func8
func8(funParam: func7Name)

// 函数作为返回值类型
func func9() -> (Int, Int) -> Bool {
    return func7Name // 将 func7Name 返回
}

// 函数嵌套
func outerFunc() {
    let outerScope = "outer scope"
    func innerFunc() {
        print(outerScope, "in inner func")
    }
    // 在外部函数内调用内部嵌套函数,在外部函数以外无法调用它
    innerFunc()
}
// 调用外部函数
outerFunc()


  1. 闭包:后置闭包、逃逸闭包与自动闭包

// 定义闭包,类似于 Kotlin 的 lambda 表达式。
// 闭包一般是为了处理回调,传递功能代码块
// 闭包标准语法结构:{(param list) -> valueType in block}
let closureFunc1 = {(param1: Int, param2: Int) -> Int in
    return param1 + param2
}

// 调用闭包
closureFunc1(1, 2) // 3

// 闭包可省略返回值
let closureFunc2 = {(param1: Int, param2: Int) in
    return param1 + param2
}
closureFunc2(1, 3) // 4


// 如果闭包只有一行代码,可以省略 return
let closureFunc3 = {(param1: Int, param2: Int) in
    return param1 < param2
}
closureFunc3(1, 2) // true


// 入参为闭包
func func10(closureParam: (Int, Int) -> Bool) {
   closureParam(2, 1)
}
// 使用默认参数名
func10(closureParam: { $0 > $1 }) // true

// 后置闭包,当最后一个参数为闭包时,简化写法:
func10() {
    $0 > $1
}

// 非逃逸闭包:函数的生命周期结束后,闭包也将被销毁
// 定义的闭包默认都是非逃逸的


// 逃逸闭包:函数的执行结束后,闭包在函数外仍可使用
// 定义逃逸闭包使用 @escaping ,一般用于异步回调
func func11(closureParam: @escaping (Int, Int) -> Bool) {
   
}

// 定义自动闭包使用 @autoclosure,对简单闭包的自动生成。
// 自动闭包默认非逃逸。自动闭包不能够有参数,单表达式
func autoCloseFunc(closureParam: @autoclosure () -> Int) {
    print(closureParam()) // 6
}
autoCloseFunc(closureParam: 1 + 2 + 3)


八) Swift5 高级[运算符]与枚举

  1. 位运算

// Swift 位运算
var sixteen: UInt8 = 0b00010000 // 二进制
print(sixteen) // 8

// Swift 按位与 & : 操作数相同的位进行逻辑与运算
// 即两个对应位的值都为1,结果为1,否则为0。示例:
var result1 = sixteen & 0b00001111 // 0
var result2 = 0b00000111 & 0b00000001 // 1

// Swift 按位或 | :操作数相同的位进行逻辑或运算
// 即两个对应位的值有一个为1,结果为1,否则为0。示例:
var result3 = 0b00000111 | 0b00000000 // 0b00000111 7
var result4 = 0b00000001 | 0b00000010 // 0

// Swift 按位取反 ~ :将操作数的每一位都取反,如果当前位是1,则取反为0
var result5 = ~sixteen // 0b11101111 255 - 16 = 239

// Swift 按位异或 ^
// 即两个对应位的值相同,结果为0,否则为1。示例:
var result6 = 0b00000111 ^ 0b00000000 // 0b00000111 7
var result7 = 0b00000111 ^ 0b00001111 // 0b00001000 8

// Swift 按位左移 << ,按位右移 >>
var result8 = 0b00000111 << 1 // 0b00001110 7 << 1 = 7 * 2 = 14
var result9 = 0b00000111 >> 1 // 0b00000011 7 >> 1 = 7 / 2 = 3

// Swift 按位左移 << ,按位右移 >> 对于位数较低类型,会出现数据丢失的情况
var result10: UInt8 = 0b00001000 << 5 // 0
var result11: UInt8 = 0b00010000 >> 5 // 0


  1. 溢出运算符

// 溢出运算符
var num: UInt8 = 255
var result12 = num &+ 1 // 溢出后变为0
result12 = result12 &- 1 // 溢出后再减1255
result12 = result12 &* 2 // 即 0b11111111 << 1 = 0b11111110 = 254

  1. 重载运算符

// 重载运算符 + , 元组相加,扩展加号的新功能
func +(param1: (Int, Int), param2: (Int, Int)) -> (Int, Int) {
    return (param1.0 + param2.0, param1.1 + param2.1)
}
var tuple1: (Int, Int) = (1, 2)
var tuple2: (Int, Int) = (1, 2)
let tuple = tuple1 + tuple2 // (2, 4)


  1. 自定义运算符

// 自定义运算符
// 自定义前缀运算符,即只需要一个操作数,运算符在操作数前面
prefix operator ++
prefix func ++(param: Int) -> Int {
    return param + 1
}
++1 // 2

// 自定义中缀运算符,即需要两个操作数,运算符在两个操作数中间
infix operator **
func **(param1: Int, param2: Int) -> Int {
    return param1 * param1 + param2 * param2
}
1 ** 2 // 5

// 自定义后缀运算符,即只需要一个操作数,运算符在操作数后面
postfix operator --
postfix func --(param: Int) -> Int {
    return param - 1
}
1-- // 0


  1. 枚举

// 定义字符串枚举,原始值类型为 String
enum Week: String {
    // 在一行中直接定义,也可以分别使用 case 定义
    case MON = "星期一", TUE = "星期二", WED = "星期三", THUR = "星期四", FRI = "星期五", SAT = "星期六", SUN = "星期日"
}

// 定义整型枚举,原始值类型为 UInt8
enum Number: UInt8 {
    // 如果原始值是整型,后续的 case 值会依次递增
    case= 1, 贰, 叁, 肆, 伍, 陆
}

print(Number.肆.rawValue) // 4

var day = Week.MON

// 构造枚举
var numTwo = Number.init(rawValue: 2)

// case 需要详尽列出
switch day {
case .MON:
    print("monday", day.rawValue) // monday 星期一
case .TUE:
    print("tuesday")
case .WED:
    print("wednesday")
case .THUR:
    print("thursday")
case .FRI:
    print("friday")
case .SAT:
    print("saturday")
case .SUN:
    print("sunday")
}

// 定义枚举,设置相关值
enum Direction {
    case left(type: Int, text: String)
    case right(type: Int, text: String)
}
var direction = Direction.left(type: 1, text: "左")
switch direction {
case let .left(type, text):
    print("type: \(type),向\(text)") // "type: 1,向左"
case let .right(type, text):
    print("type: \(type),向\(text)")
}


九) Swift 结构体与类

  1. 结构体

// 定义结构体
struct Phone {
    
    // 定义价格属性
    var price: Int
    
    // 定义品牌属性
    var brand: String
    
    // 定义型号属性
    var model: String
    
    // 定义降价促销方法
    mutating func discount() {
        price -= 800
    }
}

// 调用默认的构造方法,创建结构体实例
let iPhone = Phone(price: 6999, brand: "iPhone", model: "iPhone 13")
print("手机品牌:\(iPhone.brand), 型号:\(iPhone.model),价格:\(iPhone.price)" )

// 结构体属于值类型,用 let 修饰后无法修改 phone 的属性值
var phone = iPhone
phone.price -= 1000

// 值修改后不会影响原结构体的值,iPhone.price: 6999, phone.price: 5999
print("iPhone.price: \(iPhone.price), phone.price: \(phone.price)")




// 定义一个类
class PhoneClass {
    
    // 定义价格属性
    var price: Int = 0
    
    // 定义品牌属性
    var brand: String = ""
    
    // 定义型号属性
    var model: String = ""
    
    // 定义降价促销方法
    func discount() {
        price -= 800
    }
    
    // 当三个属性都有默认值的时候,可以不写 init
    init(price: Int, brand: String, model: String) {
        self.price = price
        self.brand = brand
        self.model = model
    }
}
// 创建 class 实例
var huaweiPhone = PhoneClass(price: 5999, brand: "huawei", model: "p40 pro")

// 类属于引用类型,变量传递后,修改值会影响引用的变量
let huaweiNewPhone = huaweiPhone
huaweiPhone.price += 1000

// 值修改后会影响原变量的值,huaweiPhone.price: 6999, huaweiNewPhone.price: 6999
print("huaweiPhone.price: \(huaweiPhone.price), huaweiNewPhone.price: \(huaweiNewPhone.price)")

十) Swift5 属性与方法

  1. Swift 存储属性

// Swift5 存储属性
class Phone {
    var system = "iOS"
    
    // 常量存储属性,一旦实例化,不能修改
    let brand: String
    
    // 存储属性,可以修改
    var price: Int
    
    // 当类被实例化时,要保证所有的属性都初始化完成,除非属性有默认值
    init(brand: String, price: Int) {
        self.brand = brand
        self.price = price
        print("brand: \(brand), price: \(price)")
    }
}

// 修改类的属性值
let iPhone = Phone(brand: "iPhone", price: 5999)
iPhone.price -= 600

// 延时存储属性:当类初始化时,延时存储属性不被初始化,当被调用时才初始化
class Consumer {
    var money: Int
    lazy var phone: Phone = Phone(brand: "iPhone", price: 6999)
    
    init(money: Int) {
        self.money = money
    }
}

// 只初始化了money
var richMan = Consumer(money: 100_000)
// 延时属性 phone 被初始化
print(richMan.phone) // brand: iPhone, price: 6999
  1. Swift 计算属性

// Swift5 计算属性
class Android {
    // 常量存储属性,一旦实例化,不能修改
    let system: String = "android"
    // 存储属性
    var version = "12"
    // api 级别
    var apiLevel: String = "31"
    
    // 计算属性,向外提供接口访问类实例的某种状态,这种状态和类实例的属性值相关联
    var info: String {
        get {
            return "system: \(system), version: \(version), level: \(apiLevel)"
        }
        set {
            // 默认使用 newValue 指代新值
            version = newValue.split(separator: "-").first?.description ?? ""
            apiLevel = newValue.split(separator: "-").last?.description ?? ""
        }
    }
    
    // 计算属性 价格,简单模拟
    var price: ClosedRange<Int> {
        get {
            // 当版本高于30时,价格在[4000,6999]之间
            if (apiLevel > "30") {
                return 4000...6999
            } else {
                return 1000...3999
            }
        }
        // 自定义传值名称 newPrice
        set(newPrice) {
            // 当价格高于3999时,版本为31
            if (newPrice.lowerBound > 3999) {
                apiLevel = "31"
                version = "12"
            } else {
                apiLevel = "30"
                version = "11"
            }
        }
    }

}

var newPhone = Android()
print(newPhone.info) // system: android, version: 12, level: 31

newPhone.info = "11-30"
print(newPhone.info) // system: android, version: 11, level: 30

newPhone.price = 4000...4999
print(newPhone.info) // system: android, version: 12, level: 31

  1. Swift 属性监听器

// Swift5 属性监听器
class iOS {
     
    var brand: String {
        // 此属性将要被赋值时会调用,默认带一个 newValue 字段。
        // 注意:初始化时不会被调用,从第二次赋值时才开始被调用
        willSet {
            print("new value : \(newValue)")
        }
        // 此属性已经被赋值后会调用,默认带一个 oldValue 字段
        didSet {
            print("old value : \(oldValue)")
        }
    }
    
    var price: Int {
        // 自定义传值名称
        willSet(newPrice) {
            print("new price : \(newPrice)")
        }
        // 自定义传值名称
        didSet(oldPrice) {
            print("old price : \(oldPrice)")
        }
    }
        
    init(brand: String, price: Int) {
        self.brand = brand
        self.price = price
        print("brand: \(brand), price: \(price)")
    }
}

let newIPhone = iOS(brand: "iphone 12", price: 5999)
newIPhone.brand = "iphone 13"
newIPhone.price = 6999



  1. Swift 属性包装器

// Swift5 属性包装器
@propertyWrapper
struct StringNotEmpty {
    var value: String
    
    init() {
        self.value = "default string"
    }
    
    var wrappedValue: String {
        get { return value }
        set {
            if (newValue.count > 0) {
                self.value = newValue
            } else {
                self.value = "default string"
            }
        }
    }
}

class Student: CustomStringConvertible {
    @StringNotEmpty
    var name: String
    
    var description: String {
        return "student's name is \(name)"
    }
}

let student = Student()
student.name = ""
print(student) // student's name is default string


  1. Swift 静态属性与静态方法

// Swift5 静态属性与静态方法
class BaseClass {
    // 静态存储属性
    static var param = "param"
    
    // 静态计算属性
    static var computeParam: String {
        return "computeParam"
    }
    
    // 可被继承的静态计算属性
    class var openParam: String {
        return "openParam"
    }
    
    // 声明静态方法,即类方法
    static func method() {
        print("static method")
    }
    
    // 声明可被继承的静态方法
    class func openMethod() {
        print("static openMethod")
    }
}

class SubClass : BaseClass {
    // 重写父类的 openParam 属性,类似于 Kotlin 的 open 修饰
    override class var openParam: String {
        return "SubClass openParam"
    }
    
    override class func openMethod() {
        print("SubClass openMethod")
    }
}

// 调用静态属性
BaseClass.param
BaseClass.computeParam
BaseClass.openParam
SubClass.openParam

// 调用静态方法
BaseClass.method()
BaseClass.openMethod()
SubClass.openMethod()


  1. Swift 下标方法

// Swift5 下标方法
// 为自定义的数据类型使用 subscript 定义下标访问元素的方法
class CustomList {
    var list: Array<String>
    init(list: String...) {
        self.list = list
    }
    
    // 定义下标方法
    subscript(index: Int) -> String {
        set {
            list[index] = newValue
        }
        // 可以只定义get
        get {
            return list[index]
        }
    }
}

let list = CustomList(list: "1", "2", "3")
list[0] = "item 1"
print(list[1])



十一) Swift5 [构造方法]

  1. Swift 类的构造方法

// Swift5 类的构造方法
// 1. 在构造方法中需要给没有默认值的属性初始化值
class Demo1 {
    // 含有默认值
    var param1: String = "default"
    
    // 未指定默认值,需要在 init() 中初始化
    var param2: String
    
    // 未指定默认值,类型为 Optional,可空,
    // 默认值为 nil, 不需要在 init() 中初始化
    var param3: String?
    
    init(param: String) {
        self.param2 = param
    }
}

// 2. 如果属性有默认值,则自动生成一个无参构造方法 init(),即初始化后的实例的属性都有默认值
class Demo2 {
    var param1 = "param1 String"
    var param2 = "param2 String"
}

var demo2 = Demo2()
print("param1: \(demo2.param1), param2: \(demo2.param2)")

  1. Swift 结构体的构造方法

// Swift5 结构体的构造方法
struct StructDemo {
    var param1: String
    var param2: String
    
    // 结构体的构造方法会默认生成,将所有属性作为参数
    init(param1: String, param2: String) {
        self.param1 = param1
        self.param2 = param2
    }
    
    init() {
        // 在自定义的构造方法中调用默认的构造方法
        self.init(param1: "init value1", param2: "init value2")
    }
}

// 调用结构体默认的构造方法
var structIns1 = StructDemo(param1: "value1", param2: "value2")

// 调用结构体自定义的构造方法
var structIns2 = StructDemo()


  1. Swift 指定构造 方法 和 便利构造 方法 指定构造方法 designated 与 便利构造方法 convenience 。使用原则:
  • 子类的指定构造方法中必须调用父类的指定构造方法
  • 便利构造方法中必须调用当前类的其他构造方法
  • 便利构造方法最终是要调用指定某个构造方法

// Swift5 指定构造方法和便利构造方法
// 定义一个含有指定构造和便利构造的父类
class Base {
    
    // 默认的指定构造方法
    init() {
        print("base class designated constructor method")
    }
    
    // 声明一个便利构造方法
    convenience init(param: Int) {
        print("base class convenience constructor method")
        // 最终调用指定构造方法
        self.init()
    }
}

var baseIns = Base(param: 0)

// 继承父类
class Sub : Base {
    
    override init() {
        // 如果重写父类的指定构造方法,必须调用 super
        super.init()
    }
    
    convenience init(param: Int) {
        // 在便利构造方法中调用指定的构造方法
        print(param)
        self.init()
    }
    
    convenience init(param1: Int, param2: Int) {
        // 在便利构造方法中调用另一个便利构造方法
        print(param1, param2)
        self.init(param: param1)
    }
}

var subIns = Sub(param1: 0, param2: 1)



  1. Swift 构造方法的安全性检查
  • 必须在调研父类的指定构造方法前完成自身属性的赋值
  • 必须在调用父类指定的构造方法之后,在子类中才能修改父类的属性值
  • 在调用父类的构造方法之后,才能使用 self 关键字
  • 在便利构造方法中要修改属性值必须在调用指定构造方法之后

// Swift5 构造方法的安全性检查
class BaseCheck {
    var field: String
    init(field: String) {
        self.field = field
    }
}

class SubCheck: BaseCheck {
    var subField: String
    
    init() {
        // 1. 必须在调研父类的指定构造方法前完成自身属性的赋值
        subField = "sub field value"
        super.init(field: "base field value")
        
        // 2. 必须在调用父类指定的构造方法之后,在子类中才能修改父类的属性值
        field = "base field set in sub"
        
        // 3. 在调用父类的构造方法之后,才能使用 self 关键字
        self.subField = "sub field value set again"
        
        print(subField, field)
    }
    
    convenience init(param: Int) {
        // 4. 在便利构造方法中要修改属性值必须在调用指定构造方法之后
        self.init()
        subField = "subField set in convenience \(param)"
        field = "field set in conveniencem \(param)"
        
        print(subField, field)
    }
}

var checkIns = SubCheck(param: 1)



  1. Swift 定义可失败的构造方法

// Swift5 定义可失败的构造方法
class CanBeNil {
    
    var field: Int
    
    // 构造可能会失败返回 nil
    init?(param: Int) {
        guard param > 0 else {
            return nil
        }
        field = param
    }
}

let ins = CanBeNil(param: 0) // nil


  1. Swift 必要构造方法 与 析构方法

// Swift5 必要构造方法与析构方法
class DemoClass {
    
    var field: Int
    
    // 声明必要构造方法,子类必须继承或重写
    required init(param: Int) {
        field = param
    }
    
    // 析构方法,类实例被销毁
    deinit {
        // 实例被释放
        print("demo instance destroy")
    }
}
// 定义可选类型
var demoIns: DemoClass? = DemoClass(param: 1)
demoIns = nil // 被赋值 nil 时,deinit会调用


十二) Swift 内存管理与异常处理

Swift5 内存引用与异常处理

  1. Swift 内存销毁时机

// Swift5 内存销毁时机
// 引用类型的内存销毁时机
class ClassDemo {
    var a = "value a"
    deinit {
        // 实例被释放
        print("deinit class a")
    }
}

// 可空类型
var ins1: ClassDemo? = ClassDemo()
var ins2 = ins1
var ins3 = ins2

ins1 = nil // 取消 ins1 引用
ins2 = nil // 取消 ins2 引用
print(String(describing: ins3?.a)) // 此处 ins3 引用的实例依然在,Optional("value a")

// 对实例引用被全部取消,ClassA 实例此处才销毁
ins3 = nil // deinit class a


  1. Swift 单向引用

// Swift5 单向引用
class ClassA {
    
    deinit {
        print("deinit ClassA")
    }
    
    func foo() {
        print("func foo in ClassA")
    }
}

class ClassB {
    // 此处引用 ClassA 的实例
    var ins: ClassA?
    
    init(ins: ClassA?) {
        self.ins = ins
    }
    
    deinit {
        print("deinit ClassB")
    }
}

var clzA: ClassA? = ClassA()
var clzB: ClassB? = ClassB(ins: clzA)

// 此处 clzA 所引用的内存并未释放
clzA = nil
// 依然可以调用 clzB 中的 clzA 实例的 foo 方法
clzB?.ins?.foo() // func foo in ClassA
// 此时 ClassB 实例被释放,不再有引用指向 ClassA 随即所占内存也被释放
clzB = nil // deinit ClassB \n deinit ClassA


  1. Swift 循环引用

// Swift5 循环引用
class ClassC {
   var insD: ClassD?
   
   deinit {
       print("deinit ClassC")
   }
   
   func foo() {
       print("func foo in ClassC")
   }
}

class ClassD {
   // 此处引用 ClassC 的实例
   var insC: ClassC?
   
   init(ins: ClassC?) {
       self.insC = ins
   }
   
   deinit {
       print("deinit ClassD")
   }
}

var clzC: ClassC? = ClassC()
var clzD: ClassD? = ClassD(ins: clzC)

clzC?.insD = clzD

// 此处 clzC 所引用的内存并未释放,对应实例被 clzD 的 insC 引用
clzC = nil
// 依然可以调用 clzD 中的 insC 实例的 foo 方法
clzD?.insC?.foo() // func foo in ClassC
// 此时 clzD 的实例依然被 clzC 的 insD 引用,clzC 和 clzD 实例都未被释放
clzD = nil



  1. Swift 弱引用 解决 循环引用 问题

// Swift5 使用 弱引用 解决 循环引用
class ClassE {
    // 弱引用 weak
    weak var insF: ClassF?
    
    deinit {
        print("deinit ClassE")
    }
    
    func foo() {
        print("func foo in ClassE")
    }
}

class ClassF {
    // 此处引用 ClassE 的实例
    var insE: ClassE?
    
    init(ins: ClassE?) {
        self.insE = ins
    }
    
    deinit {
        print("deinit ClassF")
    }
}

var clzE: ClassE? = ClassE()
var clzF: ClassF? = ClassF(ins: clzE)

clzE?.insF = clzF

// 此处 clzE 所引用的内存并未释放,对应实例被 clzF 的 insE 引用
clzE = nil
// 依然可以调用 clzF 中的 insE 实例的 foo 方法
clzF?.insE?.foo() // func foo in ClassE
// 此时 clzF 的实例被 clzE 的 insF 弱引用,会被销毁,clzE 和 clzF 实例都能被释放
clzF = nil // deinit ClassF \n deinit ClassE



  1. Swift 无主引用,针对类型为非 Optional

// Swift5 无主引用,针对类型为非 Optional
class ClassG {
    // 无主引用 unowned 假定属性不为 nil
    unowned var insH: ClassH
    
    init(ins: ClassH) {
        self.insH = ins
    }
    func foo() {
        print("func foo in ClassG")
    }
    deinit {
        print("deinit ClassG")
    }
}

class ClassH {
    // 此处引用 ClassE 的实例
    var insG: ClassG?
    
    deinit {
        print("deinit ClassH")
    }

}
var clzH: ClassH? = ClassH()
var clzG: ClassG? = ClassG(ins: clzH!)


clzH?.insG = clzG

// 此处 clzG 所引用的内存并未释放,对应实例被 clzH 的 insG 引用
clzG = nil
// 依然可以调用 clzH 中的 insG 实例的 foo 方法
clzH?.insG?.foo() // func foo in ClassG
// 此时 clzH 的实例被 clzG 的 insH 无主引用,会被销毁,clzG 和 clzH 实例都能被释放
clzH = nil // deinit ClassH \n deinit ClassG


  1. Swift 闭包产生的循环引用

// Swift5 闭包产生的循环引用
class ClassJ {
    var field = "field j"
    
    lazy var closure: () -> Void = {
        print(self.field)
    }
    
    deinit {
        print("deinit ClassJ")
    }
}

var objJ: ClassJ? = ClassJ()
objJ?.closure()
// 因为闭包引用了类的成员属性,导致实例无法释放,进而导致闭包无法释放,产生循环引用
objJ = nil // 此处并没有打印 deinit 中信息


  1. Swift 解决闭包产生的循环引用

// Swift5 解决闭包产生的循环引用
class ClassK {
    var field = "field k"
    
    lazy var closure: () -> Void = {
        // 使用捕获列表对 self 进行无主引用的转换
        [unowned self] () -> Void in
        print(self.field)
    }
    
    deinit {
        print("deinit ClassK")
    }
}

var objK: ClassK? = ClassK()
objK?.closure()
objK = nil // deinit ClassK



  1. Swift 自定义异常类型

// Swift5 自定义异常类型
enum CustomError: Error {
    case ErrorOne
    case ErrorTwo
    case ErrorThree
}

print("error")
//throw CustomError.ErrorOne // 抛出的异常未捕获会终止,不会打印 complete
print("complete")


  1. Swift do-catch 捕获异常,try 执行会抛异常的函数
// Swift5 使用 do-catch 捕获异常,try 执行会抛异常的函数
// 通过函数抛出异常
func funcError() throws -> String {
    throw CustomError.ErrorTwo
}

// 使用 do-catch 捕获异常
do {
    // 使用 try 执行可能会抛出异常的函数
    try funcError()
} catch CustomError.ErrorOne {
    print("ErrorOne")
} catch CustomError.ErrorTwo {
    print("ErrorTwo")
} catch CustomError.ErrorThree {
    print("ErrorThree")
}

// 使用 try? 将函数执行的结果映射为 Optional 类型
let result = try? funcError()
if (result == nil) {
    print("exec failed")
}

// try! 强行终止异常的传递,如果发生异常,则程序中断
// try! funcError()

  1. Swift 函数延时执行结构
// Swift5 函数延时执行结构:避免在抛异常的时候,保证某些必须的代码块要执行,如释放资源
func lazyFunc() throws -> Void {

    defer {
        // 函数结束时会得到执行
        print("lazy part of func")
    }
    
    print("exec lazyFunc")
    throw CustomError.ErrorThree
}

// exec lazyFunc
// lazy part of func
try? lazyFunc()


十三 ) Swift5 类型转换泛型、扩展与协议

1. Swift 判断值类型


// Swift5 判断值类型
var anyObj: Any = 1
if anyObj is Int {
    print("anyObj's type is Int")
} else if anyObj is String {
    print("anyObj's type is String")
}


2. Swift 判断引用类型


// Swift5 判断引用类型
class Base {
    var text = "base text"
}
class Sub1: Base {
    var subText1 = "sub1 text"
}
class Sub2: Base {
    var subText2 = "sub2 text"
    func subFunc() {
        print("sub func invoke")
    }
}


3. Swift 类型转换


// Swift5 类型转换 as , as! , as?
var obj = Base()
var subObj1 = Sub1()
var subObj2 = Sub2()

// Swift 数组中类型为 Base 的都可以存入
var arrayObj: [Base] = [obj, subObj1, subObj2]

for index in 0..<arrayObj.count {
    let obj = arrayObj[index]
    if obj is Sub1 {
        // 如果是 Sub1 就转换为 Sub1 类型,向下转型
        let subObj = obj as! Sub1
        print(subObj.subText1)
    } else if obj is Sub2 {
        let subObj = obj as! Sub2
        print(subObj.subText2)
    } else {
        print(obj.text)
    }
}


4. Swift AnyObject 与 Any


// Swift 使用 AnyObject 类型声明数组,其中可以存放任何引用类型,不可以存放值类型
var arrayAnyObj: [AnyObject] = [obj, subObj1, subObj2]

// Swift 使用 Any 类型声明数组,其中可以存放任何类型,包含 值类型 和 引用类型
var arrayAny: [Any] = [1, "2", true, obj, (0, 0), {(param: Int) -> Int in return param}]


5. Swift 泛型与扩展



// Swift 使用泛型定义方法,打印自身
func printSelf<T>(_ param: T) {
    print(param)
}
printSelf("text")
printSelf(1000)

// Swift 使用泛型定义结构体,实现简单集合
struct List<T> {
    private var datas: [T] = []
    
    mutating func add(_ newEle: T) {
        datas.append(newEle)
    }
    
    mutating func get(_ index: Int) -> T {
        return datas[index]
    }
}

// Swift 使用扩展,给结构体添加一个扩展方法
extension List {
    func getDatas() -> [T] {
        return datas
    }
}

// 定义整型的集合
var list = List<Int>()
// 添加元素
list.add(1)
list.add(2)
list.add(3)
// 读取元素
var ele = list.get(1)
print(ele) // 2

// 调用扩展方法
var datas = list.getDatas()
print(datas) // [1, 2, 3]



6. Swift 泛型约束


// Swift5 添加泛型约束
// 定义一个存放 Base 子类的结构体
struct ObjList<T: Base> {
    private var datas: [T] = []
    
    mutating func add(_ newEle: T) {
        datas.append(newEle)
    }
    
    mutating func get(_ index: Int) -> T {
        return datas[index]
    }
}
var objList = ObjList<Sub1>()
objList.add(subObj1)

// 定义多个泛型约束
class MultipleType<T, R> where T: BaseProtocol, R: SubProtocol {
}


7. Swift 协议 protocol


// Swift5 定义协议 protocol,类似于 java, kotlin 中定义接口
protocol BaseProtocol {
    // 实现协议时才指定类型
    associatedtype T
    // 定义普通方法
    func printType(input: T) -> Void
    
    // 定义计算属性
    var field1: T {get}
    var field2: String {get set}
    
    // 定义静态方法
    static func method()
}

// 协议继承,自动继承 BaseProtocol 中的属性和方法
protocol SubProtocol: BaseProtocol {
}
// ClaProtocol 协议只能被类遵守
protocol ClaProtocol: AnyObject {
}



8. Swift 遵守实现协议


// Swift5 遵守协议并实现
class ClassImpl: BaseProtocol {
    func printType(input: String) {
        print(input)
    }
    // 实现计算属性
    var field1: String {
        get {return "field1"}
        set {}
    }
    var field2: String {
        get {return "field2"}
        set {}
    }
    // 实现静态方法
    static func method() {}
}

// 为类扩展属性和方法
extension ClassImpl {
    var extField: Int {
        get {
            return 100
        }
    }
    
    func extFunc() {
        print("ext func")
    }
}

var implCla = ClassImpl()

implCla.printType(input: "input text")
print(implCla.extField)
implCla.extFunc()


9. Swift 定义协议可选实现


// 使用 @objc 定义可选实现
@objc protocol OptionalProtocol: AnyObject {
    @objc optional func method();
}

extension OptionalProtocol {
	// 利用扩展,提供默认实现
    func method() {
        print("default method extension")
    }
}

class ClassOptPro: OptionalProtocol {
}

// 调用默认实现
var claOptIns = ClassOptPro()
claOptIns.method()


十四 ) Swift 高级特性

一、Swift 独占访问

Swift 内存安全检查:当两个变量访问同一块内存时,会产生独占内存访问限制。
发生读写权限冲突的情况: inout 参数读写冲突 结构体中函数修改成员属性读写冲突 值类型属性读写冲突

1. inout 参数读写冲突


// 1. Swift inout 参数读写冲突
var inputStr = "input"
func plusSlef1(_ param: inout String) {
    // 在 >= Swift4 版本会抛异常:同时访问0x103ed30a0,但是修改需要独占访问。
    param += inputStr
}
// 调用下面的代码会崩溃
// plusSlef1(&inputStr)

// 同时访问同一个内存地址,造成读写冲突
func plusSlef2(_ param1: inout String, _ param2: inout String) {
    // 在 >= Swift4 版本会抛异常:重叠访问'inputStr',但修改需要独占访问;考虑复制到一个局部变量
    let result = param1 + param2
    print(result)
}
// 调用下面的代码会崩溃
// plusSlef2(&inputStr, &inputStr)


2. 结构体中函数修改成员属性读写冲突


// Swift 结构体中函数修改成员属性读写冲突
struct StructA {
    var field: Int
    
    // mutating 修饰可修改成员属性,inout 开放写访问,会产生读写冲突
    mutating func edit(_ param: inout StructA) {
        field = param.field
    }
}

var structA = StructA(field: 100)
// 调用下面的代码会产生编译错误:
// 1. Inout参数不允许彼此别名
// 2. 重叠访问'structA',但修改需要独占访问;考虑复制到一个局部变量
// structA.edit(&structA)


3. 值类型属性读写冲突


// Swift 值类型属性读写冲突
class ClassA {
    // 定义元组,属于值类型
    var tuple = (key1: 1, key2 : 2)
    
    func test1(_ param1: inout Int, _ param2: inout Int) {
        print(param1, param2)
    }
    
    func test2() {
        // 如果被调用会崩溃,同时访问0x600000667cd0,但是修改需要独占访问。
        test1(&tuple.key1, &tuple.key2)
    }
    
    func test3() {
        // 访问 局部 值变量 可以正常使用
        var localTuple = (key1: 3, key2 : 4)
        test1(&localTuple.key1, &localTuple.key2)
    }
}

let cla = ClassA()
// 调用下面的代码会崩溃
// cla.test2()
cla.test3() // 正常调用,打印 3 4


二、Swift 增强字符串

// 多行字符串 界定符 转义符
// Swift 多行字符串,同 kotlin 的原始字符串,不需要手动添加换行符。可用于排版
var text1 = """
start
\(1)
2
end
"""
print(text1)


// 转义符
var text2 = "对单引号进行转义\'"
print(text2) // 对单引号进行转义'

// 使用界定符代替转义符
var text3 = #"对单引号进行转义'"#
print(text3) // 对单引号进行转义'

// 使用界定符时,转义符失去作用
var text4 = #"换行符1 \n 换行符2"#
print(text4) // 换行符1 \n 换行符2

// 使用界定符时,使用 \# 保留转义符的作用
var text5 = #"换行符1 \#n 换行符2"#
print(text5) // 会换行打印: 换行符1 换行符2


三、Swift 动态成员查找


@dynamicMemberLookup // Swift使用 @dynamicMemberLookup 为类增加动态查找成员的能力
@dynamicCallable // Swift使用 @dynamicCallable 为类增加动态方法调用的能力
class Data {
    var field1: Int = 0
    var field2: String = ""
    
    subscript(dynamicMember member: Int) -> String {
        return "class don't have the field: \(member), type int"
    }
    
    subscript(dynamicMember member: String) -> String {
        return "class don't have the field: \(member), type String"
    }
    
    // 传入一组参数
    func dynamicallyCall(withArguments argArray: [Int]) {
        print("invoke unknown func with args: \(argArray)")
    }
    
    // 传入键值对参数
    func dynamicallyCall(withKeywordArguments pairs: KeyValuePairs<String, Int>) {
        let argPairs = pairs.map{ key, value in
            return "\(key): \(value)"
        }
        print(argPairs)
    }
}

let data = Data()
// 当访问不存在的属性时,就会调用对应的 subscript 方法返回对应类型的值
// class don't have the field: someInt, type String. class don't have the field: someString, type String
print(data.someInt, data.someString)


// 调用不存在的方法,把实例当做方法调用
// 传入一组参数
data(1, 2, 3) // invoke unknown func with args: [1, 2, 3]
// 传入键值对参数
data(key1: 1, key2: 2) // ["key1: 1", "key2: 2"]


四、Swift 增强协议


// Swift 使用多个协议界定参数
protocol ProtocolA {
    var field: String {get set}
}
protocol ProtocolB {
    func method()
}
// 实现多个协议
class ClassImpl : ProtocolA, ProtocolB {
    var field: String = "impl field"
    
    func method() {
        print("impl method")
    }
}
// 使用 & 界定多个协议
func testImpl(impl: ProtocolA & ProtocolB) {
    print(impl.field)
    impl.method()
}

testImpl(impl: ClassImpl())


四、Swift 增强协议

// Swift 使用多个协议界定参数 protocol ProtocolA { var field: String {get set} } protocol ProtocolB { func method() } // 实现多个协议 class ClassImpl : ProtocolA, ProtocolB { var field: String = "impl field"

func method() {
    print("impl method")
}

} // 使用 & 界定多个协议 func testImpl(impl: ProtocolA & ProtocolB) { print(impl.field) impl.method() }

testImpl(impl: ClassImpl())

参考文献

blog.csdn.net/java_androi…

猜你喜欢

转载自juejin.im/post/7233326040088625209