iOS开发_swift 5.1入门基础语法
1. 基本数据类型
-
1.1 常量和变量
// 常量 let a = 10 // 变量 var b = 11.1
-
1.2 类型安全和类型推断
- let 和 var 定义常量,编译器可以根据具体的值,来推断类型。
- swift 是强类型语言,编译的时候如果赋值类型和声明类型不一致会报错。
-
1.3 基本类型
// 基本类型 let aInt: Int = 10 let aFloat: Float = 10.1 let aDouble: Double = 10.0 let aBool: Bool = true let aString: String = "a" let aWrapString:String = """ 百日依山尽, 黄河入海流。 """ let aChar: Character = "a"
- swift 中一切皆对象,所以基本类型也是类类型,也需要构造器转换。
// 类型转换: 一切皆对象,利用构造器转换 let aLong: Int64 = Int64(aFloat)
- swift 中一切皆对象,所以基本类型也是类类型,也需要构造器转换。
-
1.4 类型别名
// 类型别名 typealias MyInt = Int let myInt: MyInt = 10
-
1.5 元组
- 元组是swift中新类型,python中也有这个类型。
// 元组 let tuple = (1,"json","errMsg") tuple.0 tuple.1 tuple.2
-
1.6 可选类型
- 可选类型:可能有值,可能没有值。
- 其他类型:必须是有值的。
- 可选类型:没有初始化,默认值是nil。
- 其他类型:在使用前必须初始化。
- 1.6.1 普通可选类型和隐式解析可选类型
// 普通可选:类型? let aString: String? = "普通可选类型" // 普通可选获取值,必须强制解析 let aStr = aString! type(of: aStr) // -> String // 隐式解析可选类型: 类型! let bString: String! = "隐式解析可选类型" // 隐式解析可选类型,在定义时就制定了非空值,所以可以直接取值。 let bStr = bString type(of: bStr) // -> String
- 1.6.2 可选绑定
let someOptional: String? = "hell world" if let constantName = someOptional { // String print(type(of: constantName)) print(constantName) } else { // 绑定失败 } type(of: bStr) // -> String
- 1.6.3 可选链
- 在java OC等语言中,没有可选类型,也没有可选调用链。所以会有 if else 的各种嵌套。
if (person != null) { if (person.name != null) { print(person.name.length()) } }
- 在swift中语言中,可选调用链,整个链条上的值都可能是可选类型,如果值是nil,则终止后面的调用直接返回nil。
class Person { var mac: Mac? init?(mac: Mac?) { guard let mac = mac else { return nil } self.mac = mac } // swift中的下标语法,使得获取某些值更加便捷。 subscript(index: String) -> Int { switch index { case "count": return self.mac?.name.count ?? 0 default: return 0 } } } class Mac { let name: String = "mac book pro" } let person = Person(mac: nil) print(person?.mac?.name.count ?? 0) print(person?["count"] ?? 0)
-
1.7 运算符
- 算术运算符:+ - * / %
- 比较运算符:== != > < >= <=
- 三元运算符:问题 ?答案1 : 答案2
- 逻辑运算符: ! && ||
- 区间运算符:
// a..<b(半开区间) for i in 0..<5 { } // a...b(闭区间) for i in 0...5{ } // 单侧区间 let names = ["a","b","c"] for name in names[...2] { print(name) } for name in names[1...] { print(name) }
- 空合运算符:
- a ?? b // a != nil ? a! : b
-
1.8 断言
- 断言主要用于测试程序。
- assert(布尔表达式,“断言失败的信息”)
let age = 3 assert(age < 0,"age > 0")
- 断言和异常的区别:
- 断言用在哪些你知道绝对不会发生的事情上,来捕捉程序员自己的错误。
- 异常捕捉用户或者环境的错误。
-
1.9 宏定义
- swift中没有宏定义,OC中的宏定义会转为swift中全局常量 。
2. 字符串
- 字符串是结构体类型。
- 在swift中结构体和枚举类型都是值类型的。值类型的数据在传参的时候是进行拷贝的。保证了数据安全性。
// 定义字符串
let aString = "hello"
let bString = """
百日依山尽,
黄河入海流。
"""
// 字符串拼接
var mutableString = "hello"
mutableString += "world"
mutableString.append(" han meimei")
// 字符串插值 \(表达式或者变量)
print("hello world \(type(of: mutableString))")
print("hello world \(mutableString + String(1))")
// 字符串长度
mutableString.count
// 大小写转化
mutableString.lowercased()
mutableString.uppercased()
// 前缀后缀
mutableString.hasPrefix("hello")
mutableString.hasSuffix("world")
// 是否相等
aString == bString
3. 集合类型
-
集合是泛型。
-
可变集合:将集合赋值给 var 型变量。
-
不可变集合:将集合赋值给 let 型变量。
-
注意:swift中的不可变集合和Java中不可变集合不一样的。swift不可变集合是真的不可变。 Java中的不可变集合是引用地址不能变,但是集合可以添加删除元素。
-
3.1 Array
// 简单语法 var someInts = [Int]() // 泛型数组语法 someInts = Array<Int>() someInts.append(3) // 空数组 someInts = [] // 重复数组 var threeDoubles = Array(repeating:0.0, count:3) // 数组连接 var anotherThreeDoubles = Array(repeating:2.4, count:3) var sixDoubles = threeDoubles + anotherThreeDoubles // 字面常量 var shopping = [1, 3, 4, 5] // 是否为空 shopping.isEmpty shopping[1] shopping.insert(3, at:0) shopping.removeLast() // 变量数组 for item in shopping { print(item) } for (index, value) in shopping.enumerated() { print("item \(String(index + 1)) : \(value)") }
-
3.2 Set
- 一个类型存在Set中,该类型必须是可哈希化的。相等的对象 hashValue 必须相同。、
a == b a.hashValue == b.hashValue
- 所有的基本类型默认都是可哈希化的。因此可以作为Set的类型或者字典键的类型。
- 可哈希化的类型,必须遵循Hashable 协议,实现 == 方法 和 hashValue值的放回。
// 定义Set var letters = Set<String>() var set: Set<String> = [] // 插入值 letters.insert("a") // 清空元素 letters = [] // 字面量创建集合 var favorite: Set<String> = ["hello", "world"] // 删除 favorite.remove("hello") // 判断是否包含元素 favorite.contains("hello") // 遍历集合 var set: Set<String> = ["我","是","最","棒","的"] for item in set { if item == "是" { set.remove(item) } }
- 注意:swift中的集合是可以遍历删除,Java中的集合如果遍历删除会出发fast-fail, 所以Java的变脸删除一般都是迭代器删除。
-
3.3 Dictionary
- swift 的字典使用 Dictionary<Key, Value> 定义,其中 Key 是一种可以在字典中被用作键的类型,Value 是字典中对应于这些键所存储值的数据类型。
- 一个字典的 Key 类型必须遵循 Hashable 协议,就像 Set 的值类型。
- 你也可以用 [Key: Value] 这样简化的形式去表示字典类型。虽然这两种形式功能上相同,但是后者是首选,
// 定义一个字典 var nameOfIntergers = [Int: String]() // 空字典 nameOfIntergers = [:] // 字面量 var nameOfInt = [1:"hello", 2:"world"] // 删除 nameOfInt.removeValue(forKey:1)
4. 流程控制
-
4.1 for-in
var set: Set<String> = ["我", "是", "最", "棒", "的"] // 遍历Array和set for name in set { print(name) } // 遍历dictionary var dic = [1:"hello", 2:"world"] for (key, value) in dic { print("key = \(key), value = \(value)") } // 遍历区间 for i in 0..<set.count { print(i) } print("\n \n") // 指定步长,遍历开区间 for tickMark in stride(from: 0, to: 60, by: 5) { print(tickMark) } // j指定步长,遍历闭区间 for tickMark in stride(from: 0, through: 60, by: 5) { print(tickMark) }
-
4.2 while
while 条件 { 表达式 } repeat { } while 条件
- while 和 repeat while 的循环次数是一样的。
- swift 没有 i++。 赋值表达式没有返回值。
-
4.3 if
if 条件 { // 表达式1 } else if 条件1 { } else { }
-
4.4 guard
- 与if语句相同的是,guard也是基于一个表达式的布尔值去判断一段代码是否该被执行。
- 与if语句不同的是,guard只有在条件不满足的时候才会执行这段代码。
class Person { var mac: Mac? init?(mac: Mac?) { // mac == nil 时直接返回nil guard let mac = mac else { return nil } // mac != nil 才走下面的逻辑 self.mac = mac } }
-
4.4 switch
- swift中的switch 更强大,进行模式匹配。
- 字符串匹配
let a = "a" switch "b" { case "a": print("a") case "b": print("b") default: print("默认值") }
区间匹配
let cout = 32 switch cout { case 1...3: print("in 1...3") case 30...40: print("in 30...40") default: print("默认匹配") }
元组
let somePoint = (1,20) switch somePoint { case (0, 0): print("(0,0)") case (1, _): print("first = 1,second 随意") case (_, 20): print("first 随意,second = 20") default: print("没有匹配上") }
值绑定
let anotherPoint = (2, 0) switch anotherPoint { case (let x, 0): print("x = \(x), y = 0") case (2, let y): print("x = 2, y = \(y)") case (let x, let y): print("x = \(x),y = \(y)") }
where 附加条件
let yetAnotherPoint = (1,-1) switch yetAnotherPoint { case let (x,y) where x == y: print("x = \(x),y = \(y)") case let (x, y) where x == -y: print("x = \(x),y = \(y)") default: print("x = \(yetAnotherPoint.0),y = \(yetAnotherPoint.1)") }
符合型
let char = "a" switch char { case "a", "e", "i", "o", "u": print(char) default: print("其他char") }
穿透
var index = 10 switch index { case 10: index += 1 fallthrough default: index += 1 } // 12 穿透执行 print(index)
-
4.5 控制转移
·continue break
5. 函数
-
5.1 函数的定义
- func 函数名(参数名:类型,参数名:类型)-> 返回类型 {
// 执行体
}
func minMax(array: [Int]) -> (min:Int, max:Int){ var currentMin = array[0] var currentMax = array[0] for value in array { if value > currentMax { currentMax = value } else if value < currentMin{ currentMin = value } } return (currentMin,currentMax) } let array = [1,3,4,5,6,7,8] print("min = \(minMax(array: array).min) max = \(minMax(array:array).max)")
- func 函数名(参数名:类型,参数名:类型)-> 返回类型 {
-
5.2 隐式返回函数
-
如果函数体是一个单行return 语句,那么这个return可以省略掉。
func greeting(person: String) -> String {
"hello," + person + "!"
}
-
5.3 参数标签和参数名称
- 参数标签使代码有更强的可读性。
- 在参数名称前面指定参数标签。
- 如果没有指定参数标签,参数名称也就是参数标签。
- 下划线 _ 放到参数名称前面,可以省略掉参数标签,一般不这么用。
func someFuncation(argumentLabel parameterName: Int) -> Int { // parameterName 参数名称, argumentLabel 标签参数 } // from 是参数标签,hometown是参数名称 func greet(person:String,from hometown: String){ print(person+" from "+hometown) } greet(person: "wangbo", from: "haidian")
-
5.4 默认参数值
- 指定默认参数值,也是实现函数重载的方式。
func someFuncation(param: Int, param1: Int = 2){ } // 调用 someFuncation(param: 3) someFuncation(param: 3, param1: 3)
-
5.5 可变参数
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)
-
5.6 输入输出参数
- 函数的参数默认是常量,不能修改的。如果想要修改参数的值,就要把参数定义为输入输出参数(参数类型前 inout) 。并且传入的参数是个变量。
var ar = [Int]() // 参数类型前加 inout 表示是输入输出参数,可以修改 func add(array: inout [Int]) -> [Int] { for i in 1...5 { array.append(i) } return array } // 调用的时候实参前面加 &,表示这个参数可以被修改 for item in add(array: &ar) { print(item) }
-
5.7 函数类型
func funcation(param: Int, param1: String) -> [Int]{ } // 函数的类型 (Int,String) -> [Int]
- swift 中函数式一等公民,函数类型像其他类型一样可以定义变量, 也可以作为函数返回类型。
typealias FuncationType = (Int,Int) -> Int func some(funcation: FuncationType) -> Int { let a = 10, b = 100 return funcation(a,b) } let sum = some { (a, b) -> Int in return a + b }
-
5.8 嵌套函数
- 函数内定义函数。
func chooseStepFunction(backward: Bool) -> (Int) -> Int { func stepForward(input: Int) -> Int { return input + 1 } func stepBackward(input: Int) -> Int { return input - 1 } return backward ? stepBackward : stepForward }
6. 闭包
-
swift的闭包和OC中block,Java中Lambda一样都是用于模块之间通讯的。
-
闭包可以捕获上下文中的变量。
-
闭包就是一个匿名的函数体。
-
Java 中的lambda就是一个匿名内部类。
-
6.1 闭包表达式语法
{(parameters) -> returnType in 执行体 }
- 例子
var names = ["a","dsf","weea","3psd"] names.sort(by: { (s1: String, s2: String) -> Bool in return s1 > s2 }) for item in names { print(item) }
- swift 的表达式拥有更简洁的风格。
- 利用上下文推断参数和返回值类型
- 隐式返回单表达式闭包,单表达式闭包可以省略return关键字。
- 参数名称缩写。
- 尾随闭包语法。
- 根据上下文推断类型
- 因为闭包是作为函数的参数传入的,可以根据参数的类型来推断闭包的类型,所以闭包的 参数类型 和 返回类型 都可以省略。
names.sort(by: { s1, s2 in return s1 < s2 })
- 单表达式隐式返回
names.sort(by: { s1,s2 in s1 > s2 })
- 参数名缩写
names.sort(by:{ $0 > $1})
- 运算符方法
names.sort(by: > )
- 例子
-
6.2 尾随闭包
- 如果将闭包表达式作为最后一个参数传递给函数,将这个闭包替换成尾随闭包的形式。
- 尾随闭包不用写参数标签。
func someFunctionThatTakesAClosure(closure: () -> Void) { // 函数体部分 } // 以下是不使用尾随闭包进行函数调用 someFunctionThatTakesAClosure(closure: { // 闭包主体部分 }) // 以下是使用尾随闭包进行函数调用 someFunctionThatTakesAClosure() { // 闭包主体部分 }
-
6.3 值捕获
- 闭包可以捕获上下文中的常量或变量。
- 闭包就是函数内部与函数外部连接的桥梁。
- 闭包的用途:
- 读取函数内部的变量。
- 让这些变量的值始终在内存中。
func f1(amount: Int) -> () -> Int{ var total = 10 func f2() -> Int{ total += amount return total } return f2 } let res = f1(amount: 5) print(res()) // 15 print(res()) // 20 let res1 = f2(amount: 5) print(res()) // 15
- f1 是 f2的父函数,f2赋值给一个全局变量,f2一直在内存中,f2依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制回收。
- 闭包是引用类型,一个对象属性赋值给一个闭包,如果闭包直接访问了该对象或该对象的成员,就会引起循环引用。
-
6.4 逃逸闭包
- 闭包作为函数的参数,分为逃逸闭包和非逃逸闭包。默认是非逃逸闭包。逃逸闭包在闭包类型前面加上 @escaping 。
- 逃逸闭包的比函数体要晚执行(比如异步回调执行闭包)。
- 非逃逸闭包和函数提一起执行。
// 逃逸闭包 func sun(callBack: @escaping (String) -> Void){ DispatchQueue .global() .asyncAfter(deadline: DispatchTime.init(uptimeNanoseconds: 1000)) { callBack("我逃出sun的生命周期") } print("sun 执行完毕") } sun { print($0) } // 非逃逸闭包 func moon(callBack: (String) -> Void){ print("moon 执行") callBack("我没有逃出moon的生命周期") } moon { print($0) }
-
6.5 自动闭包
var count = 10 let f = { count += 10 } print(count) f() print(count)
7. 类
-
7.1 属性
- 7.1.1 存储属性
- 可以是 let var
- 要么默认初始化,要么init构造器初始化
- 可选类型的存储属性,没有在构造器初始化,会有默认初始化 nil
- 结构体和枚举赋值给常量,它的变量存储属性也不能修改。
class Person { let id: Int var name: String = "wangbo" // var bb: String // 如果没有默认初始化,就必须在init中初始化 // 延迟加载存储属性,第一次调用时才初始值,延迟属性必须是var lazy var array = Array(repeating: 0, count: 3) init(id: Int) { self.id = id } } // 存储属性也是可以直接修改和取值的 let person = Person(id: 10) person.name = "hh" person.array = Array(repeating: 10, count: 3) print(person.name)
- 全局变量和局部变量都是存储型变量。
- 全局变量都是延迟计算的,跟延迟存储属性类似,但是不用 lazy 标志。
- 7.1.2 计算属性
- 计算属性提供一个 getter 和一个可选的 setter方法。来间接获取或设置属性值。
- 计算属性其实就是 java 里的 getter 和 setter 方法
class Person { var _age :Int = 0 var age: Int { get { return _age } set { _age = newValue } } } let person = Person() person.age = 10 print(person.age)
- 只读计算属性
class Person { // 只读计算属性,省略掉 get{ return 10 } var age : Int { return 10 } } let person = Person() print(person.age)
- 7.1.3 属性观察器
- 属性观察器一般为存储属性提供的,计算属性可以,但是没必要,在setter方法中就可以监听变化。
class Person { var name = "hh" { willSet { print("name = \(self.name) 将变为 \(newValue)") } didSet { print("旧的值是 \(oldValue) 当前值\(self.name)") } } } let person = Person() person.name = "www"
- 7.1.4 类型属性
- static修饰的属性就是类型属性
Person.aclass Person { static let a: String = "hello" }
- 7.1.1 存储属性
-
7.2 方法
- 7.2.1 普通方法
- 结构体,枚举,类都可以有方法。
- 特殊点:swift的类方法,可以用static修饰,也可以用class修饰。
- 区别:
- static 修饰的类型方法不能被复写。
- class 修饰的类方法可以被子类复写。
class Person { static let a: String = "hello" // 实例方法 func work(at where: String) -> Void { print("at \(`where`)" world) } // 不可以被复写的类方法 static func sayHello() { print("hello") } // 可以被子类复写的类方法 class func sayWorld() { print("world") } } class Teacher: Person { override class func sayWorld() { print("teacher say world") } }
- 7.2.2 构造器
- 构造器没有返回值。
- swift 的类,如果存储属性没有指定默认值,就必须要有构造器。
class ShoppingListItem { var name: String? var quantity = 1 var purchased = false } var item = ShoppingListItem()
- 7.2.3 指定构造器和便利构造器
- 一个类必须有一个指定构造器
- 便利构造器必须依赖于指定构造器
class Person { let a: Int var b: String // 指定构造器 init(a: Int, b:String) { self.a = a self.b = b } // 便利构造器 convenience init(a: Int) { self.init(a: a, b:"haha") } } let pe = Person(a: 10) let p2 = Person(a: 12,b:"world")
- 7.2.4 可失败构造器
- 可失败构造器一般用于结构体,表示model层数据可能失败。
- 构造器通过返回一个 nil 表示这是个可失败构造器。
struct Person { let name: String init?(name: String) { if name.isEmpty { return nil } self.name = name } } let person = Person(name: "") // Optional<Person> print(type(of: person))
- 7.2.5 必要构造器
class SomeClass { required init() { // 表明子类必须实现该构造器 } }
- 7.2.1 普通方法
-
7.3 析构器
deinit(){ // 释放资源 }
-
7.4 继承
- swift的类如果不指定父类,是没有父类的。不像其他语言默认继承 Object类。
- 子类可以继承父类的属性,方法,并重写父类的属性和方法。
- 方法前加 final 修饰符,这个方法不能被重写。
- 类前加 final 修饰符,这个类不能被继承。
-
7.5 类型转换
- 7.5.1 判断类型 is
class Person { let name: String init(name: String) { self.name = name } } class Teacher: Person { var age: Int init(name: String,age: Int) { self.age = age super.init(name: name) } } let teacher = Teacher(name: "hh", age: 29) if teacher is Person { print("teacher 是Person的子类") }
- 7.5.2 类型转换 as? as!
let teacher = Teacher(name: "hh", age: 29) guard let tea = (teacher as? Person) else { print("转换失败") } print("转换成功")
- as? 表示可能转换失败,返回nil。as! 表示一定能转换成功。
-
7.6 扩展
- 扩展可以在不访问一个类,结构体,枚举,源代码的基础上,给类,结构体,枚举添加一些属性,方法,实现协议等。替代之前的工具类。
- 添加计算型实例属性和计算型类属性
- 定义实例方法和类方法
- 提供新的构造器
- 定义下标
- 定义和使用新的嵌套类型
- 使已经存在的类型遵循(conform)一个协议
extension SomeType { // 在这里给 SomeType 添加新的功能 }
- 在项目中,扩展经常用来格式化代码和实现工具类。
- 7.6.1 扩展属性
extension Double { var km: Double { return self * 1_000.0 } var m: Double { return self } var cm: Double { return self / 100.0 } var mm: Double { return self / 1_000.0 } var ft: Double { return self / 3.28084 } }
- 7.5.2 扩展构造器
struct Size { var width = 0.0, height = 0.0 } struct Point { var x = 0.0, y = 0.0 } struct Rect { var origin = Point() var size = Size() } extension Rect { init(center: Point, size: Size) { let originX = center.x - (size.width / 2) let originY = center.y - (size.height / 2) self.init(origin: Point(x: originX, y: originY), size: size) } }
- 7.5.3 扩展方法
extension Int { func repetitions(task: () -> Void) { for _ in 0..<self { task() } } mutating func square() { self = self * self } }
- 7.5.4 扩展下标
extension Int { subscript(digitIndex: Int) -> Int { var decimalBase = 1 for _ in 0..<digitIndex { decimalBase *= 10 } return (self / decimalBase) % 10 } }
- 7.5.5 嵌套类型
extension Int { enum Kind { case negative, zero, positive } var kind: Kind { switch self { case 0: return .zero case let x where x > 0: return .positive default: return .negative } } }
-
7.6 嵌套类
- swift中的嵌套类型,跟Java中的静态内部类一样。
class Person { var age: Int = 0 class Kind{ var name: String? init?(name: String?) { guard let name = name else { return nil } self.name = name } } } let kind = Person.Kind(name: nil) print(kind?.name ?? "")
-
7.7 访问控制
- open > public > interal > fileprivate > private
- open: 包以外的类可以访问,也可以继承,override方法。
- public:包以外的类可以访问,但是不能继承,override方法。
- internal: 包访问级别。相当于package。默认可写可不写。
- fileprivate: 访问级别所修饰的属性或者方法在当前的 Swift 源文件里可以访问。
- private:只有同一个类中的方法属性才能访问。
8. 结构体
- 类和结构的相同点:
- 都有存储属性和计算属性
- 都可以定义方法
- 定义下表操作
- 定义构造器
- 通过扩展以增加默认实现之外的功能
- 遵循协议
- 类和结构的不同点:
- 类是引用类型,结构体是值类型
- 结构体不允许继承,就像final class一样,是个常量类。
- 结构体可以不定义构造器,默认会有一个按照属性次序定义的构造器。
- swift中除了class是引用类型,之外的都是值类型。
- 值类型赋值给 let 常量之后,即使属性是可变属性也可以修改。
- 引用类型赋值给 let 常量之后,可变属性还是可以修改的。
- 值类型的比较 == != 相等。
- 引用类型的比较 === !== 相同。
struct PersonInfo{ let name: String var age: Int func say(word: String) -> Void { print(word) } mutating func add() -> Int { age = age + 1 return age } } let person = PersonInfo(name: "haha", age: 10)
- 结构体的普通方法是不能修改属性的,只能在构造器中修改属性值。如果强行修改,只需在方法前加上 mutating 修饰符。
- 结构体如果没有定义构造器,会有一个默认的逐一成员构造器。
9. 枚举
-
9.1 枚举语法
enum { case north case south case east case west }
-
9.2 枚举成员的集合
enum Beverage: CaseIterable { case coffee,tea,juice } for item in Beverage.allCases{ print(item) }
-
9.3 原始值
- 枚举成员在定义时可以被默认值他填充。填充的值就是原始值,原始值的类型就是枚举继承的类型。
enum Type: String{ case ALL = "all" case NORMAL = "normal" }
-
9.4 原始值隐式赋值
// Int 是原始值类型,隐式原始值是递增的 enum Planet: Int { case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune } // String 是原始值类型,隐式原始值为成员名 enum CompassPoint: String { case north, south, east, west } // rawValue属性可以访问枚举成员的原始值 print(Planet.venus.rawValue) // 输出2 print(CompassPoint.north.rawValue) // 输出north // 使用原始值初始化枚举值 let planet = Planet(rawValue: 3) // Planet.earth
-
9.5 枚举值的关联值
- 感觉用的不多
// 枚举值的关联值 enum BarCode { case upc(Int, Int, Int, Int) case qrCode(String) } let productCode = BarCode.upc(1, 23, 39, 219) let productCode1 = BarCode.upc(3, 8, 9, 78) switch productCode { case let .upc(x, y, z, q): print("x= \(x), y = \(y) ,z = \(z) ,q = \(q)") default: print("默认值") }
10. 协议
-
10.1 属性
- 协议里可以有属性,遵循这个协议的类结构体枚举都必须要有这个属性。
- 必须是 var
- 类型后面要有
protocol SomeProtocol{ var name: String {get set} static var here: String { get } } class Person: SomeProtocol{ var name: String static var here: String = "person" init(name: String) { self.name = name } } let person = Person(name: "hh") print(person.name) person.name = "ww" print(Person.here)
-
10.2 方法
protocol SomeProtocol { // 值类型修改属性的方法必须是 mutating修饰 mutating func changeName() -> String } class Person: SomeProtocol { func changeName() -> String { return "hehe" } } struct Man: SomeProtocol { var name: String mutating func changeName() -> String { name = "hhh" return name } }
-
10.3 协议合成
- 协议组合使用 SomeProtocol & AnotherProtocol 的形式。你可以列举任意数量的协议,用和符号(&)分开。
protocol Named { var name: String { get } } protocol Aged { var age: Int { get } } struct Person: Named, Aged { var name: String var age: Int } func wishHappyBirthday(to celebrator: Named & Aged) { print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!") } let birthdayPerson = Person(name: "Malcolm", age: 21) wishHappyBirthday(to: birthdayPerson)
-
10.4 协议扩展
- 协议可以通过扩展来为遵循协议的类型提供属性、方法以及下标的实现。
- 计算属性提供默认实现
- 方法提供默认实现
- extension Protocol where Self: 类 限定了协议Protocol的扩展实现,只能由类的对象调用
protocol SomeProtocol { // 普通方法 func add(age: Int) -> Int // 属性 var age: Int{get set} // 构造器 init(age: Int) } // 协议扩展 extension SomeProtocol { // 实现计算属性 var name: String { return "hhh" } // 实现方法 func work(here: String) -> String { return "我在 \(here) 工作" } } // 协议扩展实现的限定,只能由Person对象调用 extension SomeProtocol where self: Person { func sleep(time: String) { print("睡觉) } } class Person: SomeProtocol { func add(age: Int) -> Int { self.age = self.age + age return self.age } var age: Int required init(age: Int) { self.age = age } } let person = Person(age: 10) person.add(age: 10) person.work(here: "北京")
11. 泛型
-
11.1 类结构体泛型
struct Stack<T>{ var items:[T] = [T]() var count: Int{ return items.count } mutating func push(item: T) { items.append(item) } mutating func push(items: [T]){ for item in items { self.items.append(item) } } mutating func pop() -> T? { return items.popLast() ?? nil } } var stack = Stack<Int>() stack.push(item: 1) stack.push(items:[2,3,4,5]) stack.count
-
11.2 typealias 支持泛型
typealias StringDictionary<T> = Dictionary<String, T> typealias DictionaryOfStrings<T : Hashable> = Dictionary<T, String> typealias IntFunction<T> = (T) -> Int typealias Vec3<T> = (T, T, T)
-
11.3 协议泛型
protocol SomeProtocol { associatedtype T }
12. 异常处理
- 在 Swift 中,错误用遵循 Error 协议的类型的值来表示。这个空协议表明该类型可以用于错误处理。
-
12.1 错误的定义
- 错误实现Error协议的类。一般用枚举值表示。
enum VendingMachineError: Error { case invalidSelection //选择无效 case insufficientFunds(coinsNeeded: Int) //金额不足 case outOfStock //缺货 }
-
12.2 错误处理
- 12.2.1 do-catch
do { try expression statements } catch pattern 1 { statements } catch pattern 2 where condition { statements } catch { statements }
- catch 之后是模式匹配。
- 12.2.2 try?
func someThrowingFunction() throws -> Int { // ... } let y: Int? do { y = try someThrowingFunction() } catch { y = nil } // x 是个可选类型,效果和上面一样,try?是个简洁的语法 let x = try? someThrowingFunction()
- 12.2.3 try!
- 禁止错误的传递。try!认为后面的表达式不会抛出错误。
let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
- 12.2.4 throw
- 错误的传递
func canThrowErrors() throws -> String func canThrowErrors() throws
- 12.2.1 do-catch
13. 内存管理
- swift 和 OC都是引用计数管理内存,当一个对象的引用计数 = 0 时,就会被立即回收内存。
- JAVA语言是垃圾回收器,只有内存压力触发垃圾回收器回收时,才会清理内存。
- 所以,swift的内存利用率更高。JAVA内存利用低容易触发OOM。
-
13.1 弱引用
- 弱引用是个可选类型,引用对象可以是个nil。不会持有的对象保存强引用。
class Person { let name: String init(name: String) { self.name = name } var apartment: Apartment? deinit { print("\(name) is being deinitialized") } } class Apartment { let unit: String init(unit: String) { self.unit = unit } weak var tenant: Person? deinit { print("Apartment \(unit) is being deinitialized") } }
-
13.2 无主引用
- 无主引用不是可选类型,必须有一个值。也不会对持有的对象强引用。
class Customer { let name: String var card: CreditCard? init(name: String) { self.name = name } deinit { print("\(name) is being deinitialized") } } class CreditCard { let number: UInt64 unowned let customer: Customer init(number: UInt64, customer: Customer) { self.number = number self.customer = customer } deinit { print("Card #\(number) is being deinitialized") } }
-
13.3 闭包引起的循环引用
- 获列表中的每一项都由一对元素组成,一个元素是 weak 或 unowned 关键字,另一个元素是类实例的引用(例如 self)或初始化过的变量(如 delegate = self.delegate)
lazy var someClosure = { // 捕获列表 [unowned self, weak delegate = self.delegate] (success, errorCode, errorMsg) in // 这里是闭包的函数体 }