重新再学一遍Swift,现在是3.1版本.
默默的吐槽一下,Swift更新好快,下半年还要出Swift4,不过还是得跟上步伐,哈哈
常量和变量
let number: Int = 10
var numberstring: String = "hello world"
整数
let value1 = Int8.min
let value2 = UInt32.max
//分为有符号整数,无符号整数类型
浮点数
let value3: Float = 5.20
let value4:Double = 3.141592654
//Double代表 64 位的浮点数。15位精确度
//Float 代表 32 位的浮点数。6位精确度
数值型字面量
let decimalInteger = 17
//一个十进制数,没有前缀
let binaryInteger = 0b10001
//一个二进制数,前缀是 0b
let octalInteger = 0o21
//一个八进制数,前缀是 0o
let hexadecimalInteger = 0x11
//一个十六进制数,前缀是 0x
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0
//十进制的浮点字面量还有一个可选的指数,用大写或小写的 e 表示
//十六进制的浮点字面量必须有指数,用大写或小写的 p 来表示
整数类型转换
let one:Int16 = 22
let two:Int32 = 77
let sum1 = two + Int32(one)
//SomeType(ofInitialValue) 是调用 Swift 类型初始化器并传入一个初始值的默认方法
let three:Int8 = 33
let four:Double = 33.33
let sum2 = Double(three) + four
//浮点转换为整数必须显式地指定类型
//浮点转换为整数也必须显式地指定类型
类型别名
typealias Integer16 = Int16
//类型别名可以为已经存在的类型定义了一个新的可选名字
//用 typealias 关键字定义类型别名
布尔值
let success = true
let fail = false
元组
let http404 = (statusCode: 404: Int, description: "Not Found": String)
//元组把多个值合并成单一的复合型的值。元组内的值可以是任何类型,而且可以不必是同一类型
可选项
//可选项说明这里要么有一个值,要么值缺失
//例如在类型转换的时候,类型可能转换失败,初始化器可能会构造失败,变量或者常量将类型声明成可选型,就可以处理失败的情况了
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
//nil
//nil是可选项值缺失的一种特殊类型,任何类型的可选项都可以设置成nil
//nil 不能用于非可选的常量或者变量
//可选项的强制展开
//可以利用 if 语句通过比较 nil 来判断一个可选中是否包含值
if let converedNumber != nil {
print("convertedNumber has an integer value of \(convertedNumber!).")
//可选项绑定
//使用可选项绑定来判断可选项是否包含值,如果包含就把值赋给一个临时的常量或者变量
//使用 if 进行可选项绑定格式:
if let constantName = someOptional {
statements
}
if let tempValue = Int(possiableNumber) {
print("\'\(possibleNumber)\' has an integer value of \(actualNumber)")
} else {
print("\'\(possibleNumber)\' could not be converted to an integer")
}
//隐式展开可选项
在声明的类型后边添加一个叹号( String! )而非问号( String? ) 来书写隐式展开可选项
错误处理
//在函数声明过程当中加入 throws 关键字来表明这个函数会抛出一个错误
//当你调用了一个可以抛出错误的函数时,需要在表达式前预置 try 关键字
//Swift 会自动将错误传递到它们的生效范围之外,直到它们被 catch 分句处理
//do 语句创建了一个新的容器范围,可以让错误被传递到到不止一个的 catch 分句里
func makeASandwich() throws {
}
do {
try makeASandwich()
eatASandwich()
} catch Error.OutOfCleanDishes {
washDishes()
} catch Error.MissingIngredients(let ingredients) {
buyGroceries(ingredients)
}
断言
//在任何判断条件可能为假但是一定要绝对地为真才能让代码继续执行的时候才使用断言,进行调试
//断言可以“断言”一个条件是否为真。你可以使用断言确保在运行其他代码之前必要的条件已经被满足。
//如果条件判断为 true,代码运行会继续进行;如果条件判断为 false,代码运行结束,你的应用也就中止
//断言允许附加一条调试的信息
//如何使用断言:
//使用全局函数 assert(_:_:) 函数来写断言。
//向 assert(_:_:) 函数传入一个结果为 true 或者 false 的表达式以及一条会在结果为 false 的时候显式的信息
let age = 16
assert(age > 18, "用户属于未成年人")
基本运算符
1. 赋值运算符( a = b )
2. 算数运算符
- 加 ( + )
- 减 ( - )
- 乘 ( * )
- 除 ( / )
Swift 算术运算符默认不允许值溢出,可以使用 Swift 的溢出操作符(比如 a &+ b )来行使溢出行为
3. 余数运算符
余数运算符( a % b )可以求出多少个 b 的倍数能够刚好放进 a 中并且返回剩下的值(就是我们所谓的余数)
4. 一元减号运算符( - ),一元加号运算符 ( + )
5. 组合赋值符号
+= -= /= *= %=
6. 比较运算符
相等 ( a == b )
不相等 ( a != b )
大于 ( a > b )
小于 ( a < b )
大于等于 ( a >= b )
小于等于 ( a <= b )
等价运算符( === 和 !== ),可以使用它们来判断两个对象的引用是否相同
7. 三元条件运算符
( a ? b : c )
8. 合并空值运算符
( a ?? b )如果可选项 a 有值则展开,如果没有值,是 nil ,则返回默认值 b 。表达式 a 必须是一个可选类型。表达式 b 必须与 a 的储存类型相同
短路计算: 如果 a 的值是非空的, b 的值将不会被考虑
9. 区间运算符
闭区间运算符: ( a...b )
半开区间运算符: ( a..
控制流语句
//for 循环
for index in 1...9{
print("\(index) times 9 is \(index * 9)")
}
//stride(from:to:by:) 函数来跳过不想要的标记
//while循环
//while 循环通过判断单一的条件开始。如果条件为 true ,语句的合集就会重复执行直到条件变为 false
while condition {
statements
}
//repeat-while 循环,在判断循环条件之前会执行一次循环代码块。然后会继续重复循环直到条件为 false
repeat {
statements
} while condition
// if else 条件语句
//switch 匹配语句
switch some value to consider {
case value 1:
respond to value 1
case value 2,
value 3:
respond to value 2 or 3
default:
otherwise, do something else
}
//Swift 里的 switch 语句不会默认从每个情况的末尾贯穿到下一个情况里
//每一个情况的函数体必须包含至少一个可执行的语句
//一个 switch 情况中匹配多个值可以用逗号分隔,并且可以写成多行
//可以使用元组来在一个 switch 语句中测试多个值,使用下划线( _)来表明匹配所有可能的值
//switch 情况可以将匹配到的值临时绑定为一个常量或者变量
//switch 情况可以使用 where 分句来检查额外的情况
转移控制语句
//continue: continue 语句告诉循环停止正在做的事情并且再次从头开始循环的下一次遍历。它是说“我不再继续当前的循环遍历了”而不是离开整个的循环
//break: break 语句会立即结束整个控制流语句。当你想要提前结束 switch 或者循环语句或者其他情况时可以在 switch 语句或者循环语句中使用 break 语句
//fall through: 从每个情况的末尾贯穿到下一个情况中
//标签语句: 在循环前加上开始标签,在控制语句前加上结束标签
label name: while condition {
statements
}
//提前退出: 使用 guard: 一个条件必须是真才能执行 guard 之后的语句
//guard 语句总是有一个 else 分句—— else 分句里的代码会在条件不为真的时候执行
//如果 guard 语句的条件被满足,代码会继续执行直到 guard 语句后的花括号
func greet(person: [String: String]) {
guard let name = person["name"] else {
return
}
print("Hello \(name)!")
guard let location = person["location"] else {
print("I hope the weather is nice near you.")
return
}
print("I hope the weather is nice in \(location).")
}
greet(["name": "John"])
// prints "Hello John!"
// prints "I hope the weather is nice near you."
greet(["name": "Jane", "location": "Cupertino"])
// prints "Hello Jane!"
// prints "I hope the weather is nice in Cupertino."
字符串和字符
//字符串字面量
let someString = "Some string literal value"
//空字符串的初始化
var emptyString = ""
var anotherEmptyString = String()
//string是值类型,传递的是拷贝值
// 操作字符
//通过在 for-in循环里遍历 characters属性访问 String 中的每一个独立的 Character值
//字符串插值
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
//字符串字面量中的特殊字符
//转义特殊字符 \0 (空字符), \\ (反斜杠), \t (水平制表符), \n (换行符), \r(回车符), \" (双引号) 以及 \' (单引号)
//任意的 Unicode 标量,写作 \u{n},里边的 n是一个 1-8 个与合法 Unicode 码位相等的16进制数字
//字符统计 使用 characters.count
//字符范围: 使用characters.indices
//访问和修改字符串:
Index startndex endIndex index(before:) index(after: ) index(_:offsetBy:)
//删除和插入:
//要给字符串的特定索引位置插入字符,使用 insert(_:at:)方法,另外要冲入另一个字符串的内容到特定的索引,使用 insert(contentsOf:at:) 方法
//要从字符串的特定索引位置移除字符,使用 remove(at:)方法,另外要移除一小段特定范围的字符串,使用 removeSubrange(_:) 方法:
//字符串比较
//字符串和字符相等性 == !=
//前缀和后缀相等性
//要检查一个字符串是否拥有特定的字符串前缀或者后缀,调用字符串的 hasPrefix(_:)和 hasSuffix(_:)方法,它们两个都会接受一个 String 类型的实际参数并且返回一个布尔量值
数组Array
//创建数组
let array1 = [Int]() //空数组
let array2 = [Array](repeating: 3,count: 3)
//传给初始化器对应类型的默认值(叫做 repeating)和新数组元素的数量(叫做 count):
//访问和修改数组
//count isEmpty append(_: ) insert(_:at:) remove( at: ) removeLast()
//遍历数组
enumerated()方法返回数组中每一个元素的元组,包含了这个元素的索引和值
for (index, value) in shoppingList.enumerated() {
print("Item \(index + 1): \(value)")
}
// Item 1: Six eggs
// Item 2: Milk
// Item 3: Flour
// Item 4: Baking Powder
// Item 5: Bananas
合集Set
//合集将同一类型且不重复的值无序地储存在一个集合当中。当元素的顺序不那么重要的时候你就可以使用合集来代替数组,或者你需要确保元素不会重复的时候
//创建合集
var letters = Set()
print("letters is of type Set with \(letters.count) items.")
//使用数组字面量创建合集
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
//访问和修改合集
count isEmpty insert(_: ) remove(_: ) removeAll() contains(_:)
//遍历合集
//Swift 的 Set类型是无序的。要以特定的顺序遍历合集的值,使用 sorted()方法,它把合集的元素作为使用 < 运算符排序了的数组返回
for genre in favoriteGenres.sorted() {
print("\(genre)")
}
//合集操作
//使用 intersection(_:)方法来创建一个只包含两个合集共有值的新合集;
//使用 symmetricDifference(_:)方法来创建一个只包含两个合集各自有的非共有值的新合集;
//使用 union(_:)方法来创建一个包含两个合集所有值的新合集;
//使用 subtracting(_:)方法来创建一个两个合集当中不包含某个合集值的新合集。
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:)方法来判断两个合集是否拥有完全不同的值。
let houseAnimals: Set = ["", ""]
let farmAnimals: Set = ["", "", "", "", ""]
let cityAnimals: Set = ["", ""]
houseAnimals.isSubset(of: farmAnimals)
// true
farmAnimals.isSuperset(of: houseAnimals)
// true
farmAnimals.isDisjoint(with: cityAnimals)
// true
字典Dictionary
//创建字典
//Dictionary,其中的 Key是用来作为字典键的值类型, Value就是字典为这些键储存的值的类型。
//简写: [key: value]
var namesOfIntegers = [Int: String]() //空字典
//用字典字面量创建字典
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
//访问和修改字典
//count isEmpty removeValue(forKey:)
//使用字典的 updateValue(_:forKey:)方法来设置或者更新特点键的值
//updateValue(_:forKey:)方法返回一个字典值类型的可选项值
//使用下标脚本语法给一个键赋值 nil来从字典当中移除一个键值对:
//遍历字典
for (airportCode, airportName) in airports {
print("\(airportCode): \(airportName)")
}
//通过访问字典的 keys和 values属性来取回可遍历的字典的键或值的集合
for airportCode in airports.keys {
print("Airport code: \(airportCode)")
}
// Airport code: YYZ
// Airport code: LHR
for airportName in airports.values {
print("Airport name: \(airportName)")
}
函数 Func
//1. 定义和调用函数
func greetAgain(person: String) -> String {
return "Hello again, " + person + "!"
}
print(greetAgain(person: "Anna"))
//2. 函数的形式参数和返回值
无形式参数的函数
func sayHelloWorld() -> String {
return "hello, world"
}
print(sayHelloWorld())
多形式参数的函数
func greet(person: String, alreadyGreeted: Bool) -> String {
if alreadyGreeted {
return greetAgain(person: person)
} else {
return greet(person: person)
}
}
print(greet(person: "Tim", alreadyGreeted: true))
// Prints "Hello again, Tim!"
无返回值的函数
func printAndCount(string: String) -> Int {
print(string)
return string.characters.count
}
func printWithoutCounting(string: String) {
let _ = printAndCount(string: string)
}
printAndCount(string: "hello, world")
// prints "hello, world" and returns a value of 12
printWithoutCounting(string: "hello, world")
// prints "hello, world" but does not return a value
多返回值的函数
func minMax(array: [Int]) -> (min: Int, max: Int) {
var currentMin = array[0]
var currentMax = array[0]
for value in array[1.. currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
可选元组返回类型
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.. currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
print("min is \(bounds.min) and max is \(bounds.max)")
}
3. 函数实际参数标签和形式参数名
可以指定实际参数标签
可以使用默认的参数标签(即是形式参数名)
可以使用_下划线忽略标签
可以给形式参数赋一个默认值,在调用函数时可以省略参数名
使用...表示可变形式参数,可以传入多个参数
输入输出形式参数
在形式参数定义开始的时候在前边添加一个 inout关键字可以定义一个输入输出形式参数。
输入输出形式参数有一个能输入给函数的值,函数能对其进行修改,还能输出到函数外边替换原来的值。
调用函数时,在参数前面加上&,表明可以被函数修改
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// prints "someInt is now 107, and anotherInt is now 3"
函数类型
每一个函数都有一个特定的函数类型,它由形式参数类型,返回类型组成。
可以用作函数类型参数,和返回函数类型
内嵌函数
函数可以内可以嵌套函数(局部函数)
属性Property
1. 存储属性
存储属性是一个作为特定类和结构体实例一部分的常量或变量
延迟存储属性的初始值在其第一次使用时才进行计算(lazy 标注)
2. 计算属性
类、结构体和枚举也能够定义计算属性,提供一个读取器和一个可选的设置器来间接得到和设置其他的属性和值
//如果一个计算属性的设置器没有为将要被设置的值定义一个名字,那么他将被默认命名为 newValue
struct AlternativeRect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}
}
}
3. 属性观察者
属性观察者会观察并对属性值的变化做出回应。每当一个属性的值被设置时,属性观察者都会被调用,即使这个值与该属性当前的值相同。
willSet 会在该值被存储之前被调用。
didSet 会在一个新值被存储后被调用。
4. 全局和局部变量
计算属性和观察属性的能力同样对全局变量和局部变量有效
全局变量是定义在任何函数、方法、闭包或者类型环境之外的变量。
局部变量是定义在函数、方法或者闭包环境之中的变量
5. 类型属性
实例属性是属于特定类型实例的属性。每次你创建这个类型的新实例,它就拥有一堆属性值,与其他实例不同。
可以定义属于类型本身的属性,不是这个类型的某一个实例的属性。这个属性只有一个拷贝,无论你创建了多少个类对应的实例。这样的属性叫做类型属性
使用 static 关键字来开一类型属性。对于类类型的计算类型属性,你可以使用 class 关键字来允许子类重写父类的实现
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 1
}
}
enum SomeEnumeration {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 6
}
}
class SomeClass {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 27
}
class var overrideableComputedTypeProperty: Int {
return 107
}
}
查询和设置类型属性
类型属性使用点语法来查询和设置,与类型属性一致。总之,类型属性在类里查询和设置,而不是这个类型的实例
print(SomeStructure.storedTypeProperty)
// prints "Some value."
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)
// prints "Another value."
print(SomeEnumeration.computedTypeProperty)
// prints "6"
print(SomeClass.computedTypeProperty)
// prints "27"
方法Method
1. 实例方法
方法 是关联了特定类型的函数
实例方法 是属于特定类实例、结构体实例或者枚举实例的函数
实例方法默认可以访问同类下所有其他实例方法和属性。实例方法只能在类型的具体实例里被调用
使用 self属性来在当前实例当中调用它自身的方法
在实例方法中修改值类型
结构体和枚举是值类型。默认情况下,值类型属性不能被自身的实例方法修改。
需要在特定的方法中修改结构体或者枚举的属性,你可以选择将这个方法异变
使用mutating 关键字
struct Point {
var x = 0.0, y = 0.0
mutating func moveByX(deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
print("The point is now at (\(somePoint.x), \(somePoint.y))")
// prints "The point is now at (3.0, 4.0)"
在异变方法里指定自身
异变方法可以指定整个实例给隐含的 self属性。
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY)
}
}
2. 类型方法
定义在类型本身调用的方法。这类方法被称作类型方法。
你可以通过在 func关键字之前使用 static关键字来明确一个类型方法。
类同样可以使用 class关键字来允许子类重写父类对类型方法的实现。
闭包
### 什么是闭包
闭包是代码中引用和传递的功能性的独立模块
### 闭包形式:
全局函数是一个有名字但不会捕获任何值的闭包;
内嵌函数是一个有名字且能从其上层函数捕获值的闭包;
闭包表达式是一个轻量级语法所写的可以捕获其上下文中常量或变量值的没有名字的闭包。
### 闭包的优化:
- 利用上下文推断形式参数和返回值的类型;
- 单表达式的闭包可以隐式返回;
- 简写实际参数名;
- 尾随闭包语法
### 闭包表达式语法:
在很简短的一行里面写完的一种语法
{ (parameters) -> (return type) in
statements
}
闭包的函数整体部分由关键字 in 导入,这个关键字表示闭包的形式参数类型和返回类型定义已经完成,并且闭包的函数体即将开始
sorted(by:)://会根据你提供的排序闭包将已知类型的数组的值进行排序,返回新数组
let names = ["Chris","Alex","Ewa","Barry","Daniella"]
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reversedNames = names.sorted(by: backward)
// reversedNames is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
提供排序闭包的一个方法是写一个符合其类型需求的普通函数,并将它作为 sorted(by:) 方法的形式参数传入
` reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )`
- 从语境中推断类型:
` reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )`
- 从单表达式闭包隐式返回:
`reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )`
- 简写的实际参数名:Swift 自动对行内闭包提供简写实际参数名,你也可以通过 $0 , $1 , $2 等名字来引用闭包的实际参数值
` reversedNames = names.sorted(by: { $0 > $1 } )`
- 运算符函数简写:Swift 的 String 类型定义了关于大于号( >)的特定字符串实现,让其作为一个有两个 String 类型形式参数的函数并返回一个 Bool 类型的值
`reversedNames = names.sorted(by: >)`
### 尾随闭包:
当需要将一个很长的闭包表达式作为函数最后一个实际参数传递给函数,使用尾随闭包将增强函数的可读性。尾随闭包是一个被书写在函数形式参数的括号外面(后面)的闭包表达式:
` reversedNames = names.sorted() { $0 > $1 }`
如果闭包表达式作为函数的唯一实际参数传入,而你又使用了尾随闭包的语法,那你就不需要在函数名后边写圆括号了:
` reversedNames = names.sorted { $0 > $1 }`
### 捕获值:
一个闭包能够从上下文捕获已被定义的常量和变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍能够在其函数体内引用和修改这些值
### 引用类型闭包:
无论你什么时候安赋值一个函数或者闭包给常量或者变量,你实际上都是将常量和变量设置为对函数和闭包的引用
### 逃逸闭包:
当闭包作为一个实际参数传递给一个函数的时候,我们就说这个闭包逃逸了,因为它可以在函数返回之后被调用。
**使用关键字: \@escaping 明确闭包是逃逸的,使用时,必须显式的引用self**
闭包可以逃逸的一种方法是被储存在定义于函数外的变量里,例如: 多函数接收闭包实际参数来作为启动异步任务的回调。函数在启动任务后返回,但是闭包要直到任务完成——闭包需要逃逸,以便于稍后调用
### 自动闭包:
自动闭包是一种自动创建的用来把作为实际参数传递给函数的表达式打包的闭包。它不接受任何实际参数,并且当它被调用时,它会返回内部打包的表达式的值。这个语法的好处在于通过写普通表达式代替显式闭包而使你省略包围函数形式参数的括号
自动闭包允许你延迟处理,因此闭包内部的代码直到你调用它的时候才会运行
**使用关键字: \@autoclosure 标志标记它的形式参数使用了自动闭包**