31 类型转换
- is 判断是否为某种类型, 包括
协议 - as? 转换成功, 可选类型, 失败为nil
- as! 转换成功, 会解包为非可选, 失败崩溃
- as 转换类型, 强制转换
// is 判断类型
var stu: Any = 10
print(stu is Int) // true
stu = "Jack"
print(stu is String) // true
stu = Student ()
print(stu is Person) // true print(stu is Student) // true print(stu is Runnable) // true
// as? as! 转换类型
var stu: Any = 10
(stu as? Student)?.study() // 没有调用study 没有崩溃
(stu as! Student).study() // 直接崩溃
stu = Student()
(stu as? Student)?.study() // Student study
(stu as! Student).study() // Student study
(stu as? Runnable)?.run() // Student run
// as 强制类型转换
var data = [Any]()
data.append(Int("123") as Any)
var d = 10 as Double
print(d) // 10.0
32 类型
X.self是一个元类型(metadata )的指针,metadata存放着类型相关信息 ( MyPerson.self )
X.self属于X.Type类
32.1 类型声明及使用
let p: MyPerson.Type = MyPerson.self // 声明类型和赋值类型
let s = MyStudent.self
print(p == MyPerson.self) // true
print(s == MyStudent.self) // true
// MyPerson.Type 定义了类型, 看着很鸡肋~~~
32.2 Anyclass 等同于 AnyObject.Type
// Anyclass (代表类类型) 标准库定义的
public typealias AnyClass = AnyObject.Type
// AnyClass
var c: AnyObject.Type = MyPerson.self
c = MyStudent.self
print(c) // MyStudent
var c1: AnyClass = MyPerson.self
c1 = MyStudent.self
print(c1) // MyStudent
let p2 = MyPerson()
print(type(of: p2) == MyPerson.self)
32.3 类型使用场景
// 通过类名初始化对象
func creatObject(objectClass: UIViewController.Type) -> AnyObject {
return objectClass.init()
}
let controller: TestViewController = creatObject(objectClass: TestViewController.self) as! TestViewController
self.navigationController?.pushViewController(controller, animated: true)
33 Self
Self代表当前类, 多用作返回值
// 协议声明方法返回 Self, 与实现
protocol Runnable {
func test () -> Self //代表方法需要返回, 调用者同类型的对象
}
class Person : Runnable {
required init() {}
func test() -> Self {
type(of: self).init() // 类型的对象
}
}
34 自定义错误
throw & do catch & rethrows
// 定义错误枚举
enum LoginError: Error {
case invalidEmail
case invalidPassword(reason: String)
case networkError(code: Int)
case unknown
}
// 注意 LoginError枚举, 遵守了 Error 协议
// 如果不遵守 Error 协议, 不能抛出错误, throw LoginError.invalidEmail
// 定义结构体 类行不行 ? 可以 但是要继承Error协议
// 定义抛出错误方法
func login(email: String, password: String) throws -> String {
// 校验 邮箱
guard email.contains("@") else {
throw LoginError.invalidEmail
}
// 校验密码
guard password.count >= 6 else {
throw LoginError.invalidPassword(reason: "密码至少6位")
}
// 模拟网络错误
let statusCode = 404
if statusCode != 200 {
throw LoginError.networkError(code: statusCode)
}
return "登录成功"
}
// 注意 方法名称 throws, 函数体 throw
// 直接处理报错
login(email: "", password: "") // 包含throws直接调用不行,需要处理错误
// ❌ Call can throw, but it is not marked with 'try' and the error is not handled
// ❌ 调用可能会抛出错误,但未使用 'try' 标记,且未对错误进行处理
// 调用方法
// try catch 处理
do {
let result = try login(email: "hahaxixi@126.com", password: "123456")
print(result)
} catch LoginError.invalidEmail {
print("邮箱格式错误")
} catch LoginError.invalidPassword(let reason) {
print("密码错误:\(reason)")
} catch LoginError.networkError(let code) {
print("网络错误,状态码:\(code)")
} catch LoginError.unknown {
print("未知错误") // 单独处理 unknown
} catch {
print("其他错误:\(error)") // 兜底
}
// 注意最后catch 中的 error,
// 当进入catch 块时,Swift 会自动创建一个名为 error 的常量,表示被抛出的错误对象。
35 defer 延迟关键字
作用: 语句将延迟到当前作用域结束之前执行
func deferTest() -> Int {
defer {
print("我来说两句") // 代码退出前执行
}
print("今天天气不错")
return 1
}
let num = deferTest()
print(num)
// 今天天气不错
// 我来说两句
// 1
36 try & try? & try!
- try 需要手动处理错误
- try? 系统自动帮我们处理异常,如果方法成功返回可选项,异常返回nil
- try! 代码告诉系统,我这段代码不可能有异常,你大胆用。可如出现异常,系统直接崩溃。
// 定义错误枚举
enum AgeError: Error {
case tooYoung
case tooOld
}
// 定义抛出错误方法
func checkAge(_ age: Int) throws -> Int{
guard age >= 18 else {
throw AgeError.tooYoung
}
guard age <= 60 else {
throw AgeError.tooOld
}
return age
}
// 1 try 使用
do {
let age = try checkAge(15)
print("通过验证",age)
} catch AgeError.tooYoung {
print("年龄太小")
} catch AgeError.tooOld {
print("年龄太大")
} catch {
print("其他错误--",error)
}
// 2 try? 用法
if let age = try? checkAge(25) {
print("验证通过", age)
}
// 3 try! - 确定不会错(慎用)
let age = try! checkAge(23) // 确定有值
print("验证通过", age)
37 断言 assert
作用: 手写个崩溃条件, 调试代码
- 默认情况下,Swift的断言只会在Debug模式下生效,Release模式下会忽略
func devide(a: Int, b: Int) -> Int {
if b == 0 {
assert(b != 0, "除数不能是0")
}
return a / b
}
// 注意 assert(condition: Bool) 条件false 打印
38 泛型
作用: 泛指任何类型
38.1 基本用法 函数
func swapValues<T> (_ a: inout T, _ b: inout T) {
(a, b) = (b, a)
}
// 如果上述方法分别处理string int double 三种类型, 需要写三个方法, 用泛型一次搞定
var a = 10
var b = 20
swapValues(&a, &b)
print(a,b) //20 10
38.2 泛型类型
✅ 泛型结构体 struct<T>
✅ 泛型枚举 enum<T>
✅ 泛型类 class<T>
❌ 协议 protocol 不能泛型 Swift 没有泛型协议,只能用关联类型 associatedtype
// 泛型结构体
// 泛型可以遵守协议
struct Stack<T: Animal> {
private var elements = [T]() // 泛型数组
// 入栈
mutating func push(_ item: T) {
elements.append(item)
}
// 出栈
mutating func pop() -> T? {
elements.popLast()
}
// 取栈顶
func top() -> T? {
elements.last
}
}
// 为啥用 mutating ? 值类型要修改属性值
// 泛型枚举
enum Result<T> {
case success(T)
case failure(String)
}
// 泛型类
class Stack<T> {
private var elements = [T]() // 空泛型数组 [T]()
func push(_ item: T) {
elements.append(item)
}
func pop() -> T? {
elements.popLast()
}
func top() -> T? {
elements.last
}
}
// 为啥用 不用 mutating ? 引用类型要修改属性值 不需要
38.3 关联类型 associatedtype
作用: 关联类型的唯一目的就是, 让协议能够拥有"泛型"的能力。
38.3.1 关联类型协议作用
// ❌ 协议不能这样写(语法错误)
protocol Container<T> {
func add(_ item: T)
}
// ✅ 用关联类型实现
protocol Container {
associatedtype Item // 占位符,具体类型由实现者决定
func add(_ item: Item)
func get() -> Item?
}
38.3.2 关联协议用法
// 定义一个数据提供者协议
protocol DataProvider {
associatedtype DataType
func fetchData() -> DataType
func updateData(_ data: DataType)
}
// 网络数据提供者
class NetworkDataProvider: DataProvider {
typealias DataType = Data // 确定协议 DataType 类型
func fetchData() -> Data {
// 从网络获取数据
return Data()
}
func updateData(_ data: Data) {
// 更新网络数据
}
}
38.4 泛型类型约束
作用: 约束泛型, 例如遵守某个协议, 继承自某个类
protocol Runnable { }
class Person { }
// 泛型 需继承自Person 并遵守 Runnable协议
func swapValues <T : Person & Runnable>(_ a: inout T, _ b: inout T) {
(a, b) = (b, a)
}
38.5 不透明类型 some
- 不透明: 编译器知道类型, 调用者不知道类型 ( 仅仅知道遵守了某协议 )
声明 some 表示 仅仅返回一中遵守协议的类,不混着来
解决:
-> 背景: 协议中带关联值的 / 声明的方法返回Self, 创建方法中协议作为返回值/参数,
-> 解决: 通过some修饰, 告诉编译器仅仅返回一种类型
38.5.1 普通返回协议
// 协议 不带关联值 或 不带返回Self方法
protocol Animal {
func sound() -> String
}
struct Dog: Animal {
func sound() -> String { "汪汪" }
func wagTail() { print("摇尾巴") }
}
struct Cat: Animal {
func sound() -> String { "喵喵" }
func climb() { print("爬树") }
}
// 可以返回 两种类型
func makeAnimal(isDog: Bool) -> Animal {
if isDog {
return Dog()
} else {
return Cat()
}
}
let animal = makeAnimal(isDog: true)
print(animal.sound()) // 汪汪
// animal.wagTail() // ❌ 编译错误:不知道是 Dog,只知道遵循 Animal
38.5.1 返回协议带关联值 返回值处理
// 协议 带关联值
protocol Runnable {
associatedtype Speed
var speed: Speed { get }
}
class Person : Runnable {
var speed: Double { 0.0 }
}
class Car : Runnable {
var speed: Int {0}
}
// 混合调用
func get(isPerson: Bool) -> Runnable {
// ⚠️ Use of protocol 'Runnable' as a type must be written 'any Runnable'; this will be an error in a future Swift language mode
// ⚠️ 将协议“Runnable”作为类型使用时,必须写为“any Runnable”;这在未来的Swift语言模式中将会是一个错误
if isPerson == true {
return Person()
} else {
return Car()
}
}
// 返回带警告⚠️, 明确在未来的swift版本会报错!
// 核心问题是 swift 编译中所有类型必须确定, 所以报错/警告
// 通过 some 声明仅返回一种协议类型, 编译器知道, 但是调用者不知道
func get(isPerson: Bool) -> some Runnable { // // 返回值 some 处理
return Person()
}
// 通过 any 声明仅返回, 编译器不知道是什么类型,只知道它遵守协议
func get(isPerson: Bool) -> any Runnable {
return Person()
}
38.5.2 协议带关联值, 参数处理
protocol Runnable {
associatedtype Speed
}
class Dog : Runnable {
typealias Speed = Double
}
class Person {
var pet: some Runnable { // 参数 some 处理
return Dog()
}
}
39 可选项本质
- 可选项是个枚举
@frozen public enum Optional<Wrapped> : ~Copyable, ~Escapable where Wrapped : ~Copyable, Wrapped : ~Escapable {
case none
case some(Wrapped)
public init(_ value: consuming Wrapped)
public mutating func take() -> Wrapped?
public static func ~= (lhs: _OptionalNilComparisonType, rhs: borrowing Wrapped?) -> Bool
public static func == (lhs: borrowing Wrapped?, rhs: _OptionalNilComparisonType) -> Bool
public static func != (lhs: borrowing Wrapped?, rhs: _OptionalNilComparisonType) -> Bool
public static func == (lhs: _OptionalNilComparisonType, rhs: borrowing Wrapped?) -> Bool
public static func != (lhs: _OptionalNilComparisonType, rhs: borrowing Wrapped?) -> Bool
}
// some
let age: Int? = 10
let age1: Optional<Int> = Optional<Int>.some(10)
print(age == age1) // true
let age2: Optional = .some(10)
let age3 = Optional.some(10)
print(age2 == age3) // true
// none
var age4 = Optional(10)
age4 = nil
age4 = .none
print(age4 == nil) // true
let age5: Int? = nil
let age6 = Optional<Int>.none
let age7: Optional<Int> = .none
print(age5 == age7) // true
40 运算符重载
类、结构体、枚举可以为现有的运算符提供自定义的实现,这个操作叫做:运算符重载
// 类中 重写 + 运算符
class Test {
struct Point {
x: Int, y: Int
}
func + (pl: Point, p2: Point) -> Point {
Point (x: p1.x + p2.x, y: p1.y + p2.y)
}
}
let p = Point(x: 10, y: 20) + Point(x: 11, y: 22)
print(p) // Point(x: 21, y: 42)
41 Equatable
public protocol Equatable {
/// 返回一个布尔值,指示两个值是否相等。
/// 左, 右为要比较的值
static func == (lhs: Self, rhs: Self) -> Bool
}
struct Point: Equatable{
var x: Int, y: Int
}
let p1 = Point(x: 10, y: 10)
let p2 = Point(x: 10, y: 10)
print(p1 == p2) // true
// print(p1 == p2) // ❌ 若未继承Equatable 二元运算符 '==' 无法应用于两个 'Point' 操作数
42 Comparable 协议
Comparable 协议用于定义类型实例之间的顺序和比较规则
public protocol Comparable : Equatable {
/// 返回一个布尔值,指示第一个值的真伪
/// - Parameters:
/// - lhs: A value to compare. 一个比较的值
/// - rhs: Another value to compare. 另一个比较的值
static func < (lhs: Self, rhs: Self) -> Bool
static func <= (lhs: Self, rhs: Self) -> Bool
static func >= (lhs: Self, rhs: Self) -> Bool
static func > (lhs: Self, rhs: Self) -> Bool
}
struct Person: Comparable {
let name: String
let age: Int
// 只需要实现 < 运算符,其他运算符会自动生成
static func < (lhs: Person, rhs: Person) -> Bool {
// 按年龄排序,年龄小的更小
return lhs.age < rhs.age
}
}
// 按照Int 来定义大小
let alice = Person(name: "Alice", age: 25)
let bob = Person(name: "Bob", age: 30)
print(alice < bob) // true (25 < 30)
print(alice > bob) // false
// lhs 左边数 rhs 右边数, 代表 int 左边的小于右边的,
43 自定义运算符
Swift 允许你定义全新的运算符,而不仅仅是重载现有的(如 +、<)。
// 定义前缀 运算符
prefix operator ±
// 定义中缀 运算符
infix operator **
// 定义后缀 运算符
postfix operator ~~
prefix func ± (value: Int) -> Int {
value * 2
}
func ** (lhs: Int, rhs: Int) -> Int {
Int(pow(Double(lhs), Double(rhs))) // pow 代表 前者的后者次幂
}
postfix func ~~ (value: Int) -> Int {
value * value
}
class TestViewController: UIViewController {
var num: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
print(±5) // 10
print(2 ** 3) // 8
print(5~~) // 25
}
}
44 扩展 ( Extension )
Swift中的扩展,有点类似于OC中的分类(Category )
能做:
-
扩展可以为枚举、结构体、类、
协议添加新功能 -
可以添加方法、计算属性、下标、(便捷)初始化器、嵌套类型、协议等等
不能做:
-
不能覆盖原有的功能
-
不能添加
存储属性,不能向已有的属性, 添加属性观察器 -
不能添加父类
-
不能添加指定初始化器,不能添加反初始化器
ps :
- 添加存储属性, 是存储在对象中的, 会破坏内存结构
- 不能覆盖原有的方法, 和oc分类不一样
- 不能添加父类, 会继承父类的存储属性, 破坏内存结构
- dinit init 不能写, 便捷初始化器可以
44.1 扩展 添加方法 枚举 计算属性
// 定义扩展
extension Int {
// 方法
func repetitions(task: () -> Void) {
for _ in 0..<self {
task()
}
}
// 方法 修改自身属性
mutating func square() -> Int {
self = self * self
return self
}
// 枚举
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
}
}
}
// 调用
2.repetitions {
print("---") // --- ---
}
var a = 3
print(a.square()) // 9
a = 0
print(a.kind) // zero
a = 2
print(a.kind) // positive
a = -3
print(a.kind) // negative
44.2 扩展可以使用类中的 泛型
class Stack<E> {
var elements = [E]()
func push (_ element: E) {
elements.append (element)
}
func pop() -> E {
elements.removeLast()
}
func size() -> Int {
elements.count
}
}
// 扩展中依然可以使用原类型中的泛型类型
extension Stack {
func top() -> E {
elements.last!
}
// 符合条件 扩展
extension Stack : Equatable where E : Equatable {
static func == (left: Stack, right: Stack) -> Bool {
left.elements == right.elements
}
}
class Person {
var age: Int
var name: String
init(age: Int, name: String) {
self.age = age
self.name = name
}
}
// 扩展 遵守协议 添加方法 便捷初始化器
extension Person : Equatable {
// 添加 == 方法 判断相等
static func == (left: Person, right: Person) -> Bool {
left.age == right.age && left.name == right.name
}
// 添加 便捷初始化器
convenience init() {
self.init(age: 0, name: "")
}
}
如果希望自定义初始化器的同时,编译器也能够生成默认初始化器口 可以在扩展中编写自定义初始化器
required 初始化器也不能写在扩展中
struct Point {
var x: Int = 0
var y: Int = 0
}
extension Point {
init(_ point: Point) {
self.init(x: point.x, y: point.y)
}
}
var p1 = Point()
var p2 = Point (x: 10)
var p3 = Point(y: 20)
var p4 = Point(x: 10, y: 20)
var p5 = Point (p4)
45 访问控制 open& public& internal
访问控制级别从高到低(限制从少到多):
open > public > internal > fileprivate > private
独立模块介绍介绍
- Framework
- target
- 静态库.a,
- App Extension
- cocoapod中 pods中的文件夹, 例如SnapKit文件夹
45.1 internal(默认级别)
- 如果不写任何关键字,默认就是
internal。
45.2 open
- 允许在定义在实体模块, 其他模块中访问, 允许其他模块继承, 重写(open只能用在类, 类成员上)
45.3 public
- 允许在定义在实体模块, 其他模块中访问, 不允许其他模块继承, 重写
45.4 fileprivate
- 只允许在定义的源文件中访问
45.5 private
- 只允许在定义实体封闭声明中访问, 当前文件