Swift学习1 基础篇 - Adaicon/iOS-notes GitHub Wiki
https://swift.bootcss.com/02_language_guide/08_Enumerations 下面是一段OC的代码,如果要用Swift实现它,需要学习一些基础语法
NSUInteger wordSum = 0;
NSUInteger scoreSum = 0;
for (int i = 0; i < self.readResult.count; i++) {
for (int j = 0; j < self.readResult[i].evaluateWords.count; j++) {
if (self.readResult[i].evaluateWords[j].hasRead && self.readResult[i].evaluateWords[j].isChineses) {
scoreSum += 1;
}
if (self.readResult[i].evaluateWords[j].isChineses) {
wordSum += 1;
}
}
}
import UIKit
两个关键词:类型推断 、类型注释
//变量 var
var maxCacheSize = 100 //声明的时候赋值自动推断出类型
var typeAnnotation:String // 声明的时候不赋值,需要给一个类型注释。但如果你还要错误赋值...
//类型:
var a:Int = 1
var b:Double = 1.0
var c:Bool = (true && false)
var d:Float = 2.0
var e:String = "Hello, playground"
var f:Character = "f"
// UInt
var xPosition = 0.0,yPosition = 0.0,zPosition = 0.0
var spaceRule = 2+3
//别名
//typealias <#type name#> = <#type expression#>
typealias AudioSample = UInt16
var maxAmplitudeFound = AudioSample.min
// 可能和你的想法不符的一些推断
var meaningOfLife = 42 //Int 而不是 浮点型
var pi = 3.14159 // Double 而不是 Float
var anotherPi = 3 + 0.14159 //Double
var max = Int8.max //+ 1
print(spaceRule)
print("The current value of friendlyWelcome is \(spaceRule)")
//常量 let 一旦设定值后就不能再改变
let decimalInteger = 17
let binaryInteger = 0b10001 // 二进制的17
let octalInteger = 0o21 // 八进制的17
let hexadecimalInteger = 0x11 // 十六进制的17
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0 // 12.3* 2^0
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1
let _ = "匿名"
//元组 类似于一个结构体 (tuples) 主要作用是是在实现函数返回多个值
把多个值组合成一个复合值。元组内的值可以是任意类型,并不要求是相同类型
let http404Error = (404, "Not Found") //“一个类型为 (Int, String) 的元组
let http200Status = (statusCode: 200, description: "OK")
读取值的方式:
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
// 输出“The status code is 404”
print("The status message is \(statusMessage)")
//不需要的部分可以用下划线标记
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
//可以用下标访问,从0开始
print("The status code is \(http404Error.0)")
// 输出“The status code is 404”
//元组是值类型
var someScore = ("John", 55)
var anotherScore = someScore
anotherScore.0 = "Robert"
println(anotherScore.0) //Outputs: "Robert"
println(someScore.0) //Outputs: "John"
用处是处理值缺失,OC可以用nil表示缺少一个合法的对象,但是对于其他类型比如枚举、结构体是没有一个特定的值表示缺失,只能约定一个特殊值标记需要特殊处理之类的,但在Swift中用这个可选类型可处理任何类型的值缺失。
var surveyAnswer: String? // surveyAnswer 被自动设置为 nil
surveyAnswer = nil
var anotherSurveyAnswer: Optional<String> //也是可选类型
if surveyAnswer != nil {
//可选值是有值的
print("surveyAnswer has a value of \(surveyAnswer!).") // 强制解析
} else {
print("possibleNumber could not be converted to a String")
}
if let actualAnswer = surveyAnswer,let _ = Int("4") { //可选绑定
print("surveyAnswer has avalue of \(actualAnswer)")
} else {
print("possibleNumber could not be converted to a String")
}
// 自动解析 有时候在程序架构中,第一次被赋值之后,可以确定一个可选类型总会有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值
var myString:String!
myString = "Hello, Swift!"
if myString != nil {
print(myString)
}else{
print("myString 值为 nil")
}
不必给每一个枚举成员提供一个值。如果给枚举成员提供一个值(称为原始值),则该值的类型可以是字符串、字符,或是一个整型值或浮点数。 两个关键词:关联值和原始值
定义和使用
enum CompassPoint {
case north
case south
case east
case west
}
enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
var directionToHead = CompassPoint.west
var directionToHead:CompassPoint
directionToHead = .east
directionToHead = .south
switch directionToHead {
case .north:
print("Lots of planets have a north")
case .south:
print("Watch out for penguins")
case .east:
print("Where the sun rises")
case .west:
print("Where the skies are blue")
}
如何得到一个包含枚举所有成员的集合:令枚举遵循 CaseIterable 协议。Swift 会生成一个 allCases属性,用于表示一个包含枚举所有成员的集合。下面是一个例子:
enum Beverage: CaseIterable {
case coffee, tea, juice
}
let choices = Beverage.allCases
把枚举值“武装”成字典:可以给枚举值设置关联值(元组),每个枚举成员的关联值类型可以各不相同
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
“定义一个名为 Barcode 的枚举类型,它的一个成员值是具有 (Int,Int,Int,Int) 类型关联值的 upc,另一个成员值是具有 String 类型关联值的 qrCode。”
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
上面的例子创建了一个名为 productBarcode 的变量,并将 Barcode.upc 赋值给它,关联的元组值为 (8, 85909, 51226, 3)。
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
但是在同一时间只能存储这两个值中的一个。
switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
print("QR code: \(productCode).")
}
枚举成员可以被默认值(称为原始值)预填充,这些原始值的类型必须相同。
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
原始值的隐私赋值
整型也是一次增1
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
当使用字符串作为枚举类型的原始值时,每个枚举成员的隐式原始值为该枚举成员的名称。
enum CompassPoint: String {
case north, south, east, west
}
使用枚举成员的 rawValue 属性可以访问该枚举成员的原始值:
let earthsOrder = Planet.earth.rawValue
// earthsOrder 值为 3
let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection 值为 "west"
递归枚举:一个枚举的关联值是本身这种枚举类型
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
//(5+4)* 2
func evaluate(_ expression: ArithmeticExpression) -> 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)
}
}
print(evaluate(product))
不需要修改时常量化,需要它们可变时变量化
声明和使用
var someInts: [Int] = []
var someDoubles = Array<Double>()
var twoDArray = Array<Array<Int>>()
var anotherTwoDArray:[[Int]] = []
var threeDoubles = Array(repeating: 0.0, count: 3)
var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
var sixDoubles = threeDoubles + anotherThreeDoubles
var shoppingList = ["Eggs", "Milk"]
//访问和修改数组
someInts.append(3)
if shoppingList.isEmpty {
print("The shopping list is empty.")
} else {
print("the count is \(shoppingList.count)")
}
sixDoubles += anotherThreeDoubles
var firstItem = shoppingList[0]
shoppingList.insert("Maple Syrup", at: 0)
shoppingList[1...2] = ["Bananas", "Apples"]
let mapleSyrup = shoppingList.remove(at: 0)
print(shoppingList)
let sortResults = sixDoubles.sorted()
Sequence 的 enumerated() 函数。这个函数会返回一个新的序列,包含了初始序列里的所有元素,以及与元素相对应的编号。但其实它只适合用一个数字去对应每个元素的时候,详见:
https://swift.gg/2017/05/05/you-probably-don't-want-enumerated/
for (index, value) in shoppingList.enumerated() {
print("Item \(String(index + 1)): \(value)")
}
tips:Sequence是一系列相同类型的值的集合,并且提供对这些值得迭代能力,我们经常把for-in循环用到Array,Dictonary,set等数据结构中,那是因为它们都是实现了Sequence协议。Sequence的协议里只有一个必须实现的方法就是makeIterator(),它需要返回一个Iterator,
var letters = Set<Character>()
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
var favoriteGenres2: Set = ["Rock", "Classical", "Hip hop"]
//访问和修改
letters.insert("a")
letters = []// letters 现在是一个空的 Set,但是它依然是 Set<Character> 类型
// count、isEmpty、insert、remove和数组一样
if favoriteGenres.contains("Rock") {
print("I get up on the good foot.")
}
for genre in favoriteGenres.sorted() {
print("\(genre)")
}
//Swift 的 Set 类型没有确定的顺序,为了按照特定顺序来遍历一个集合中的值可以使用 sorted() 方法,它将返回一个有序数组,这个数组的元素排列顺序由操作符 < 对元素进行比较的结果来确定。
let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 并
oddDigits.intersection(evenDigits).sorted()
// [] //交
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9] // 差
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]//对称差
/*
使用“是否相等”运算符(==)来判断两个集合包含的值是否全部相同。
使用 isSubset(of:) 方法来判断一个集合中的所有值是否也被包含在另外一个集合中。
使用 isSuperset(of:) 方法来判断一个集合是否包含另一个集合中所有的值。
使用 isStrictSubset(of:) 或者 isStrictSuperset(of:) 方法来判断一个集合是否是另外一个集合的子集合或者父集合并且两个集合并不相等。
使用 isDisjoint(with:) 方法来判断两个集合是否不含有相同的值(是否没有交集)。
*/
var namesOfIntegers: [Int: String] = [:]
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
和数组一样,你在用字典字面量构造字典时,如果它的键和值都有各自一致的类型,那么就不必写出字典的类型。 airports 字典也可以用这种简短方式定义:
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
//count、isEmpty
airports["LHR"] = "London" //没有的话会自动加一个,有的话就是修改
namesOfIntegers[16] = "sixteen"
和下标的方式不同,updateValue(_:forKey:) 这个方法返回更新值之前的原值。这样使得你可以检查更新是否成功。
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
print("The old value for DUB was \(oldValue).")
}
// 输出“The old value for DUB was Dublin.”
if let airportName = airports["DUB"] {
print("The name of the airport is \(airportName).")
} else {
print("That airport is not in the airports dictionary.")
}
// 打印“The name of the airport is Dublin Airport.”
airports["APL"] = nil //用这种方式移除一个键值对
removeValue(forKey:) 方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的值或者在没有对应值的情况下返回 nil:
if let removedValue = airports.removeValue(forKey: "DUB") {
print("The removed airport's name is \(removedValue).")
} else {
print("The airports dictionary does not contain a value for DUB.")
}
for airportCode in airports.keys {
print("Airport code: \(airportCode)")
}
// Airport code: YYZ
// Airport code: LHR
for airportName in airports.values {
print("Airport name: \(airportName)")
}
如果你需要使用某个字典的键集合或者值集合来作为某个接受 Array 实例的 API 的参数,可以直接使用 keys 或者 values 属性构造一个新数组:
let airportCodes = [String](airports.keys)
// airportCodes 是 ["YYZ", "LHR"]
let airportNames = [String](airports.values)
// airportNames 是 ["Toronto Pearson", "London Heathrow"]
主要是多行的时候,格式的调整:缩紧、续行、空行 三个问题
let singleLineString = "These are the same."
//多行 \续行符
let softWrappedQuotation = """
The White Rabbit put on his spectacles. "Where shall I begin, \
please your Majesty?" he asked.
"Begin at the beginning," the King said gravely, "and go on \
till you come to the end; then stop."
"""
print(softWrappedQuotation)
看图学缩进
转义符的使用
1、转义字符 \0(空字符)、\\(反斜线)、\t(水平制表符)、\n(换行符)、\r(回车符)、\"(双引号)、\'(单引号)。
2、Unicode 标量,写成 \u{n}(u 为小写),其中 n 为任意一到八位十六进制数且可用的 Unicode 位码。
let threeDoubleQuotes = """
Escaping the first quote \"""
Escaping all three quotes \"\"\"
"""
print(threeDoubleQuotes)```
//换行
let badStart = """
one
two
"""
let end = """
three
"""
print(badStart + end)
// 打印两行:
// one
// twothree
let goodStart = """
one
two
"""
print(goodStart + end)
// 打印三行:
// one
// two
// three
扩展字符串分隔符 “# #” 这两个符号中间括起来的转义会当成字符输出,失去转义功能
let threeMoreDoubleQuotationMarks = #"Line 1 \nLine 2"#
输出Line 1 \nLine 2
如果要取消上面这种功能,匹配转义字符(\)后面添加与起始位置个数相匹配的 # 符
// 记忆的时候可以记着ji数个# 反而可以全部抵消,形如一个都没有
#"Line 1 \#nLine 2"#
###"Line1 \###nLine2"###
输出都是:Line 1
Line 2
字符串插值 字符串插值是一种构建新字符串的方式,可以在其中包含常量、变量、字面量和表达式
你可以在已有字符串中插入常量、变量、字面量和表达式从而形成更长的字符串,这一过程也被称为字符串插值。尤其是在为显示、存储和打印创建自定义字符串值时,字符串插值操作尤其有用。
\(插入)
print(#"6 times 7 is \#(6 * 7)."#)
42
字符串初始化和访问 注意:结构体、字符串、枚举都是值类型
var emptyString = "" // 空字符串字面量
var anotherEmptyString = String() // 初始化方法
let catCharacters: [Character] = ["C", "a", "t", "!", "🐱"]
let catString = String(catCharacters) //用字符来初始化
emptyString += " and carriage"
if emptyString.isEmpty {
print("")
} else {
print(emptyString.count)
}
emptyString.append("#")
for character in "Dog!🐶" {
print(character)
}
Unicode是一个用于在不同书写系统中对文本进行编码、表示和处理的国际标准,它使你可以用标准格式表示来自任意语言几乎所有的字符,并能够对文本文件或网页这样的外部资源中的字符进行读写操作。Unicode 标量是对应字符或者修饰符的唯一的 21 位数字(一个单位) 可扩展的字形群集:并不是说一个标量就一定是一个字符,有些我们看到是一个字符,但其实是多个Unicode标量组成的,比如:
let eAcute: Character = "\u{E9}" // é
let combinedEAcute: Character = "\u{65}\u{301}" // e 后面加上 ́
// eAcute 是 é, combinedEAcute 是 é
let precomposed: Character = "\u{D55C}" // 한
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ, ᅡ, ᆫ
// precomposed 是 한, decomposed 是 한
所以如果字符串里有这种扩展字形群集,cout 的值和我们看到的长度并不相同。
var word = "cafe"
print("the number of characters in \(word) is \(word.count)")
// 打印输出“the number of characters in cafe is 4”
word += "\u{301}" // 拼接一个重音,U+0301
print("the number of characters in \(word) is \(word.count)")
// 打印输出“the number of characters in café is 4”
NSString 的 length 属性是利用 UTF-16 表示的十六位代码单元数字,而不是 Unicode 可扩展的字符群集。 所以length 可能不等于 count
访问
let greeting = "Guten Tag!"
greeting[greeting.startIndex]
// G
greeting[greeting.index(before: greeting.endIndex)]
// !
greeting[greeting.index(after: greeting.startIndex)]
// u
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index]
// a
for index in greeting.indices {
print("\(greeting[index]) ", terminator: "")
}
// 打印输出“G u t e n T a g ! ”
插入
var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome 变量现在等于 "hello!"
welcome.insert(contentsOf:" there", at: welcome.index(before: welcome.endIndex))
// welcome 变量现在等于 "hello there!"
删除
welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome 现在等于 "hello there"
let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
welcome.removeSubrange(range)
// welcome 现在等于 "hello"
子字符串 使用下标或者 prefix(_:) 之类的方法 —— 就可以得到一个 Substring 的实例
let greeting = "Hello, world!"
let index = greeting.firstIndex(of: ",") ?? greeting.endIndex // 找第一个, 的位置
let beginning = greeting[..<index]
// beginning 的值为 "Hello"
// 把结果转化为 String 以便长期存储。
let newString = String(beginning)
图:
字符串比较 == 和 != == : 可扩展字形群集完全相等,意义相同认为是相等,或者字形群集不完全相等,但外观和意义相同认为是标准相等。 !=: 除了外观不相等的是不相等,外观相等,但意义不同的也是不相等。 前缀和后缀
if scene.hasPrefix("Act 1 ")
if scene.hasSuffix("Capulet's mansion")
元组的赋值运算: let (x, y) = (1, 2) = 不再有返回值 //if x = y { // // 此句错误,因为 x = y 并不返回任何值 //}
//a % b = a %(-b)
如果两个元组的元素相同,且长度相同的话,元组就可以被比较。比较元组大小会按照从左到右、逐值比较的方式,直到发现有两个值不等时停止。如果所有的值都相等,那么这一对元组我们就称它们是相等的。例如: (1, "zebra") < (2, "apple") // true,因为 1 小于 2 (3, "apple") < (3, "bird") // true,因为 3 等于 3,但是 apple 小于 bird (4, "dog") == (4, "dog") // true,因为 4 等于 4,dog 等于 dog 注意:Swift 标准库只能比较七个以内元素的元组比较函数。如果你的元组元素超过七个时,你需要自己实现比较运算符。
a...b 表示【a,b】 (a<=b) a..<b 表示[a,b) (a<b) 半开区间的实用性在于当你使用一个从 0 开始的列表(如数组)时,非常方便地从0数到列表的长度。 [a...] [...a] 可以表达往一侧无限延伸的区间 单侧区间不止可以在下标里使用,也可以在别的情境下使用 你不能遍历省略了初始值的单侧区间,因为遍历的开端并不明显
for name in names[..<2] {
print(name)
}
for name in names[2...] {
print(name)
}
for name in names[...2] {
print(name)
}
空合运算符(a ?? b)将对可选类型 a 进行空判断,如果 a 包含一个值就进行解包,否则就返回一个默认值 b。表达式 a 必须是 Optional 类型。默认值 b 的类型必须要和 a 存储值的类型保持一致。提供了一种更为优雅的方式去封装条件判断和解包两种行为,显得简洁以及更具可读性。 解释下就是:a != nil ? a! : b 注意:如果 a 为非空值(non-nil),那么值 b 将不会被计算。这也就是所谓的短路求值。
&& || ! (它们是左结合,最好用() 明确优先级)
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
print("Hello, \(name)!")
}
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
print("\(animalName)s have \(legCount) legs")
}
let base = 3
let power = 10
var answer = 1
// for _ in 1..<minutes
for _ in 1...power {
answer *= base
}
print("\(base) to the power of \(power) is \(answer)")
// 控制粒度
let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
// 每5分钟渲染一个刻度线(0, 5, 10, 15 ... 45, 50, 55)
}
let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
// 每3小时渲染一个刻度线(3, 6, 9, 12)
}
var condition:Bool = false
while condition {
//statements
}
repeat {
//statements
} while condition
var temperatureInFahrenheit = 40
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit > 35 {
print("It's not that cold. Wear a t-shirt.")
}
// API 可用性检查
if #available(iOS 10, macOS 10.12, *) {
// 在 iOS 使用 iOS 10 的 API, 在 macOS 使用 macOS 10.12 的 API
} else {
// 使用先前版本的 iOS 和 macOS 的 API
}
func greet(person: [String: String]) {
guard "name" != nil else {
return
}
}
let someCharacter: String = "cde"
switch someCharacter {
//case "dd":
case "a", "A":
print("The first letter of the alphabet")
case "z","Z":
print("The last letter of the alphabet")
case "b"..<"z":
print("medie")
fallthrough // 贯穿 // 控制转移语句:continue、break、fallthrough
default:
print("Some other character")
}
fallthrough 关键字不会检查它下一个将会落入执行的 case 中的匹配条件。fallthrough 简单地使代码继续连接到下一个 case 中的代码,这和 C 语言标准中的 switch 语句特性是一样的
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
print("\(somePoint) is at the origin")
case (_, 0):
print("\(somePoint) is on the x-axis")
case (0, _):
print("\(somePoint) is on the y-axis")
case (-2...2, -2...2):
print("\(somePoint) is inside the box")
default:
print("\(somePoint) is outside of the box")
}
let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
print("on the x-axis with an x value of \(x)")
case (0, let y):
print("on the y-axis with a y value of \(y)")
case let (x, y):
print("somewhere else at (\(x), \(y))")
}
// 输出“on the x-axis with an x value of 2”
// 分支完备,连默认分支都不需要了
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
print("(\(x), \(y)) is just some arbitrary point")
}
// 输出“(1, -1) is on the line x == -y”
label name: while condition {
statements
}
gameLoop: while square != finalSquare {
diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
switch square + diceRoll {
case finalSquare:
// 骰子数刚好使玩家移动到最终的方格里,游戏结束。
break gameLoop
case let newSquare where newSquare > finalSquare:
// 骰子数将会使玩家的移动超出最后的方格,那么这种移动是不合法的,玩家需要重新掷骰子
continue gameLoop
default:
// 合法移动,做正常的处理
square += diceRoll
square += board[square]
}
}
print("Game over!")
你可以使用 错误处理(error handling) 来应对程序执行中可能会遇到的错误条件。
声明函数的时候 加上 throws
func canThrowAnError() throws {
// 这个函数有可能抛出错误
}
调用的时候
do {
try canThrowAnError()
// 没有错误消息抛出
} catch {
// 有一个错误消息抛出
}
举例:
do {
try makeASandwich()
eatASandwich()
} catch SandwichError.outOfCleanDishes {
washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
buyGroceries(ingredients)
}
具体使用在后续的分享中有专题,此处不涉及太多
func funcName(param1:String) -> Int {
return param1.count;
}
//变型1:没有参数
func sayHelloWorld() -> String {
return "hello, world"
}
//变型2:多个参数
func greet(person: String, alreadyGreeted: Bool) -> String {
return ""
}
//变型3:没有返回值
func greet(person: String) {
print("Hello, \(person)!")
}
//变型4:多个返回值
//变型5:返回可选类型,表示可能整个元组都没有值
func minMax(array: [Int]) -> (min: Int, max: Int)? {
if array.isEmpty { return nil }
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
print(minMax(array: []))
//需要注意的是,元组的成员不需要在元组从函数中返回时命名,因为它们的名字已经在函数返回类型中指定了。
//变型6:参数标签 标签是标签,参数名是参数名,名字是独一无二的
如果你不希望为某个参数添加一个标签,可以使用一个下划线(_)来代替一个明确的参数标签。
如果一个参数有一个标签,那么在调用的时候必须使用标签来标记这个参数。
func greet(person: String, from hometown: String) -> String {
return "Hello \(person)! Glad you could visit from \(hometown)."
}
print(greet(person: "Bill", from: "Cupertino"))//如果一个参数有一个标签,那么在调用的时候必须使用标签来标记这个参数。
//参数标签在调用函数的时候使用;调用的时候需要将函数的参数标签写在对应的参数前面。参数名称在函数的实现中使用。默认情况下,函数参数使用参数名称来作为它们的参数标签。
func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
}
someFunction(1, secondParameterName: 2)
//变型7:设置默认参数值,有默认值的放后面
func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
// 如果你在调用时候不传第二个参数,parameterWithDefault 会值为 12 传入到函数体中。
}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault = 6
someFunction(parameterWithoutDefault: 4) // parameterWithDefault = 12
//变型8:可变参数:零个或多个值
func arithmeticMean(_ numbers: Double...) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
arithmeticMean(3, 8.25, 18.75)
//一个函数能拥有多个可变参数。可变参数后的第一个行参前必须加上实参标签。实参标签用于区分实参是传递给可变参数,还是后面的行参。
//https://swifter.tips/variadic/
//变型8:输入输出参数
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
//一个函数能拥有多个可变参数。可变参数后的第一个行参前必须加上实参标签。实参标签用于区分实参是传递给可变参数,还是后面的行参。
//函数指针
// 参数类型+返回值类型 定义了一种函数类型,可以做变量和常量、可以作为参数类型、返回类型
func stepForward(_ input: Int) -> Int {
return input + 1
}
func stepBackward(_ input: Int) -> Int {
return input - 1
}
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
return backward ? stepBackward : stepForward
}
//嵌套函数 可以把函数定义在另一个函数体中。
func chooseStepFunction2(backward: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int { return input + 1 }
func stepBackward(input: Int) -> Int { return input - 1 }
return backward ? stepBackward : stepForward
}
{(parameters) -> return type in
statements
}
var nameArray = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
nameArray.sort {(String, String) -> Bool in
}
func backward(s1: String, s2: String) -> Bool {
return s1 > s2
}
nameArray.sort(by: backward)
nameArray.sort { (s1: String, s2: String) -> Bool in
return s1 > s2
}
看着👆那个,先想一下,精简到什么地步你还能看懂怎么比较两个string 的大小关系?
s1 > s2
首先,s1 和 s2 两个值都是函数参数定好的,可以推断出来,所以就可以省去,变成:
nameArray.sort { s1,s2 in
return s1 > s2
}
其次,像函数中介绍的,对于单表达式的情况,连return 都是可以省略的,这里也可以,变成:
nameArray.sort {s1,s2 in s1>s2}
然后,连参数名字是可以不用起名的,只需要给他们一个能分辨的方式就可以,所以:
nameArray.sort {$0 > $1}
最后,因为string 定义了 > 这个运算符方法刚好与这里需要的函数类型相符,可以直接传递一个大于号
nameArray.sort(by: >)
你要将闭包放在最后一个参数传递给函数,
func blockTest(complete:(()-> Void)) {
complete()
}
// 不用尾随闭包调用
blockTest(complete: {
print("my name is Ada")
})
//尾随闭包调用,不用写出它的参数标签comlete
blockTest {
print("my name is Block")
}
// 使用尾随闭包,只有一个闭包参数,()也可以省去
reversedNames = names.sorted() { $0 > $1 }
reversedNames = names.sorted { $0 > $1 }
闭包可以在其被定义的上下文中捕获常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
incrementer从外围函数捕获了 runningTotal 和 amount 变量的引用,函数和闭包都是引用类型 无论你将函数或闭包赋值给一个常量还是变量,你实际上都是将常量或变量的值设置为对应函数或闭包的引用。指向闭包的引用 可以 是一个常量,而并非闭包内容本身。 为了优化,如果一个值不会被闭包改变,或者在闭包创建后不会改变,Swift 可能会改为捕获并保存一份对值的拷贝。 Swift 也会负责被捕获变量的所有内存管理工作,包括释放不再需要的变量。
当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注 @escaping,用来指明这个闭包是允许“逃逸”出这个函数的。 在函数外用一个全局变量或是其他类型保存闭包,在函数返回后的一个合适的时机再调用(延迟调用)
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够省略闭包的花括号,用一个普通的表达式来代替显式的闭包。 一个闭包的调用
func foo(_ i:() -> Int) {
print(i())
}
foo({return 42})
使用{}创建一个闭包,并把它传递到foo函数,foo函数会打印此闭包的返回结果。当然,因为返回值明确,可以省略return。让代码更加简洁
func foo(_ i:() -> Int) {
print(i())
}
foo({42})
你也常常会使用尾随闭包,把最后一行写成:
foo{42}
但如果你使用自动必报,还可以更简单:
func bar(_ i:@autoclosure () -> Int) {
print(i())
}
bar(42)
42只是一个返回整数的表达式,为什么可以传递给类型为闭包的参数呢?因为加上了@autoclosure标记后,编译器会自动把传递的表达式加上{},从而转换为闭包。这里的自动就体现在此了。要求就是表达式的返回值和闭包的返回值类型一致即可。
最后,回到开始,我们用swift 仿一下OC 代码作为结束练习:
func SumStatistics(array:[[String]]?) -> (scoreSum:Int,wordSum:Int) {
guard let readResult = array else {
return (0,0)
}
func hasRead(_ str:String)->Bool {str.hasPrefix("*")}
func isChinese(_ str:String)->Bool {
for index in str.indices {if "\u{4E00}" <= str[index] && str[index] <= "\u{9FA5}" {return true}}
return false
}
var score = 0;
var word = 0;
for i in readResult[0..<readResult.count] {
for j in i[0..<i.count] {
if hasRead(j) && isChinese(j) {
score += 1;
}
if isChinese(j) {
word += 1;
}
}
}
return (score,word)
}
print(SumStatistics(array: [["p","ang","pang"],["*明","月"],["地","上","霜"]]))