swift Part 3

13 阅读11分钟

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

  • 只允许在定义实体封闭声明中访问, 当前文件