swift中的知识总结(一)

一、associatedtype的用法

在swift中,泛型T是一个非常强大的特性,它允许我们编写灵活且可复用的代码。而当我们在 协议(Protocol) 中需要使用泛型时,associatedtype 就派上了用场。

在 Swift 的协议中,我们无法直接使用泛型 <T>,但可以使用 associatedtype 关键字来声明一个占位类型,让协议在不确定具体类型的情况下仍然能够正常使用。

1、让协议支持不同数据类型的

js 复制代码
protocol SomeProtocol {
    associatedtype SomeType // 声明一个占位类型 SomeType,但不指定具体类型。
    func doSomething(with value: SomeType)
}

// Int类型
protocol SomeProtocol {
    associatedtype Item
    mutating func doSomething(with value: Item)
    func getItem(at index: Int) -> Item
}

struct ContainerDemo: SomeProtocol {

    typealias Item = Int // 指定Item为Int类型
    private var items: [Int] = []

    mutating func doSomething(with value: Int) {
        items.append(value)
        print(value)
    }

    func getItem(at index: Int) -> Int {
        return items[index]
    }
}

// String类型
struct StringContainer: SomeProtocol {

    typealias Item = String
    private var items: [String] = []

    mutating func doSomething(with value: String) {
        items.append(value)
    }

    func getItem(at index: Int) -> String {
        return items[index]
    }
}

protocol StackProtocol {
    associatedtype Element
    mutating func push(_ item: Element)
    mutating func pop() -> Element?
}

struct IntStack: StackProtocol {

    typealias Element = Int
    private var stacks: [Int] = []

    mutating func push(_ item: Int) {
        stacks.append(item)
    }

    mutating func pop() -> Int? {
        return stacks.popLast()
    }
}

2、使用where关键词限定类型

有时候希望assocaitedtype只能是某种类型的子类或实现了某个协议 。可以使用where关键字进行类型约束

js 复制代码
protocol Summable {
    associatedtype Number: Numeric // 限定Number必须是Numeric协议的子类型( Int、Double)
     func sum(a: Number,b: Number) -> Number
}

struct myIntergerAddr: Summable {
     func sum(a: Int, b: Int) -> Int {
        return a + b
    }
}

// 使用泛型结构体遵循协议
struct myGenericSatck<T>: StackProtocol {
    
    private var elements: [T] = []
    var isEmpty: Bool {return elements.isEmpty}
    var count: Int {return elements.count}

    mutating func push(_ item: T) {
        elements.append(item)
    }

    mutating func pop() -> T? {
        return elements.popLast()
    }
}

3、associatedtype 与泛型的区别

比较项 associatedtype (协议中的泛型) 普通泛型
适用范围 只能用于 协议 可用于 类、结构体、函数
作用 让协议支持不确定的类型,由实现者决定具体类型 让类型/函数支持泛型
例子 protocol Container { associatedtype Item } struct Stack {}
限制 只能用于协议,不能直接实例化 适用于所有类型

4、什么时候使用 associatedtype

  • 当你需要创建一个通用的协议,但不想限定某个具体类型时。
  • 当不同的实现类需要指定不同的数据类型时。
  • 当你希望协议中的某些类型参数具备类型约束时(如 where 关键字)。

二、Subscript下标的用法

  • 是一种访问集合、列表或序列中元素成员的快捷方式。它允许你通过下标语法(使用方括号 [])来访问实例中的数据,而不需要调用方法。

  • 使用Subscript可以给任意类型(枚举、结构体、类)增加下标功能。

  • subscript的语法类似于实例方法,计算属性,本质就是方法

js 复制代码
// demo1
struct TimesTable {
    let multiplier: Int

    subscript(index: Int) -> Int {
        return multiplier * index
    }
}

let threeTimesTable = TimesTable(multiplier: 3)
print(threeTimesTable[6])  // 输出: 18
    
// demo2
class MyPoint {
    var x = 0.0
    var y = 0.0
    subscript(index: Int) ->Double {
        set {
            if index == 0 {
                x = newValue
            } else if index == 1 {
                y = newValue
            }
        }

        get {
            if index == 0 {
                return x
            } else if (index == 1) {
                return y
            }
            return 0
        }
    }
}
 var mmpoint = MyPoint()
  mmpoint[0] = 11.1
  mmpoint[1] = 22.2

  print(mmpoint.x)
  print(mmpoint.y)
  print(mmpoint[0])
  print(mmpoint[1])
    
  // dem3
    struct Container {
    var items: [Int] = []
    
    // 单个整数下标
    subscript(index: Int) -> Int {
        return items[index]
    }
    
    // 范围下标
    subscript(range: Range<Int>) -> [Int] {
        return Array(items[range])
    }
    
    // 可变参数下标
    subscript(indices: Int...) -> [Int] {
        return indices.map { items[$0] }
    }
}

1、subscript中定义的返回值类型决定了
2、get方法的返回值类型 set方法中的newvalue的类型

3、subscript可以接受多个参数,并且类型任意

4、subscript可以没有set方法,但是必须要有get方法,如果只有get方法,可以省略get关键字

5、可以设置参数标签

6、下标可以是类型方法

三、swift中的迭代机制Sequence、collection、Iterator、AsyncSequence

在swift中,Sequence是一个协议,表示可以被逐一遍历的有序集合。一个符合Sequence协议的类型可以使用for-in循环迭代其所有元素。

Sequence是swift集合类型(Array,Dictionary、set等)的基础协议,许多高级功能如:map、filter、 reduce都依赖于它

常见的 Sequence 类型

许多 Swift 标准库类型都符合 Sequence 协议,例如:

Array:一个有序的集合。

Set:一个无序、唯一的集合。

Dictionary:键值对集合。

Range:连续的整数范围。

String:一个字符序列。

js 复制代码
/// Sequence的核心定义
public protocol Sequence {
    /// 表示序列中元素的类型。
    associatedtype Element
    associatedtype Iterator: IteratorProtocol where Iterator.Element == Element
    /// 返回一个迭代器对象,该对象遵循 IteratorProtocol 协议,并提供逐一访问元素的功能。
    func makeIterator() -> Iterator
}

public protocol IteratorProtocol {
    associatedtype Element
    /// 每次调用时返回序列的下一个元素;如果没有更多元素可用,则返回 nil。
    mutating func next() -> Element?
}

总结:

1.Sequence只承诺"能生成迭代器",不能保证反复便利,也不保证有count

2.迭代器几乎总是是struct:值语义保证"复制一份就从头开始",不会意外共享状态

3.单趟序列完全合法;第二次makeIterator()可以返回空迭代器

js 复制代码
// 可以创建自己的类型并使符合Sequence协议,只需要实现makeIterator()方法,并返回一个符合IteratorProtocol的迭代器
// 自定义一个从n倒数到0的序列
struct myCountDownDemo: Sequence {
    
    let start: Int
    func makeIterator() -> Iterator {
        Iterator(current: start)
    }

    struct Iterator: IteratorProtocol {
        var current: Int
    
        mutating func nex() -> Int? {
            guard current >= 0 else {return nil}
            defer {current -= 1}
            return current
        }
    }
}
// 调用了myArr.makeIterator()拿到一个迭代器 反复调用iterator.next() 返回的可选值解包后赋值给item
for n in myCountDownDemo(start: 3) {
     print(n)
}

let myArr = [1,5,6,8]
for item in myArr {
    print(item)
}
// for in 实际执行的是
var iterator = myArr.makeIterator()
while let element = iterator.next() {
    print(element)
}
    
// collection可以额外保证:多次遍历且顺序稳定,提供count、endIndex、下标访问,支持切片、前缀、后缀等默认实现
// 三种安全写法

// 方法一
todoItems.removeAll{$0 == "B"}

// 方法二 先记下索引,后删除
let indexsToRemove = todoItems.indices.filter{todoItems[$0] == "B"}
for i in indexsToRemove.reversed() {
    todoItems.remove(at: i)
}

// 方法三
todoItems = todoItems.filter{$0 != "B"}
//map
var numbersArr = [3,6,8]
let squares = numbersArr.map{$0 * $0}
print(squares) // 输出 [9,36,64]

// filter过滤列表中的元素
let eventNumbers = numbersArr.filter{ $0 % 2 == 0}
print(eventNumbers) // 输出[6,8]

// reduce将列表中所有元素组合成一个值
let sum = numbersArr.reduce(0 , +)
print(sum) // 输出17

// forEach对列表中的每个元素执行操作
numbersArr.forEach{print($0)}
协议 核心能力 特点与限制 常见实现
IteratorProtocol 通过 next() 方法单向、一次性地 提供下一个元素 只进不退,遍历后即消耗。是所有迭代的基础。 通常作为 Sequence 的一部分实现,很少直接使用。
Sequence 可进行顺序迭代 (如 for-in 循环),支持 mapfilterreduce 等操作 不一定可多次遍历 ,不保证通过下标访问元素 有限序列(如数组迭代器)、无限序列(如斐波那契数列生成器)
Collection Sequence 基础上,可多次、非破坏性 访问,并支持通过下标索引 访问任意有效位置的元素 必须是有限 的,并且索引操作的时间复杂度有明确规定(如 startIndexendIndex ArrayStringDictionarySet 以及自定义的集合类型。

AsyncSequence​ 是 Swift 并发模型的重要部分,特别适合处理:

  • 异步数据流(网络请求、文件读取)
  • 实时数据(传感器数据、消息推送)
  • 分页或懒加载数据
  • 长时间运行的数据生成任务

Sequence​ 更适合:

  • 内存中的集合操作
  • 同步数据处理
  • 简单的数据转换

选择依据:如果你的数据源是异步的或会产生延迟,使用 AsyncSequence ;如果数据是同步可用的,使用 Sequence

js 复制代码
// demo1
import Foundation

// 自定义异步序列
struct AsyncCountdown: AsyncSequence {
    typealias Element = Int
    
    let count: Int
    
    // 必须实现 makeAsyncIterator()
    func makeAsyncIterator() -> AsyncIterator {
        AsyncIterator(count: count)
    }
    
    // 异步迭代器
    struct AsyncIterator: AsyncIteratorProtocol {
        var count: Int
        
        // 注意:next() 是异步的!
        mutating func next() async -> Int? {
            guard count > 0 else { return nil }
            
            // 模拟异步等待
            await Task.sleep(1_000_000_000)  // 等待1秒
            
            let value = count
            count -= 1
            return value
        }
    }
}

// demo2
// 模拟从网络获取分页数据
struct PaginatedAPISequence: AsyncSequence {
    typealias Element = [String]
    
    let totalPages: Int
    let delay: UInt64
    
    func makeAsyncIterator() -> AsyncIterator {
        AsyncIterator(totalPages: totalPages, delay: delay)
    }
    
    struct AsyncIterator: AsyncIteratorProtocol {
        let totalPages: Int
        let delay: UInt64
        var currentPage = 0
        
        mutating func next() async throws -> [String]? {
            guard currentPage < totalPages else { return nil }
            
            // 模拟网络延迟
            await Task.sleep(delay)
            
            // 模拟获取数据
            let items = (0..<10).map { "Item \(currentPage * 10 + $0)" }
            currentPage += 1
            
            return items
        }
    }
}

// 使用
func fetchPaginatedData() async throws {
    let pageSize = 10
    let apiSequence = PaginatedAPISequence(totalPages: 5, delay: 500_000_000)
    
    for try await page in apiSequence {
        print("收到页面数据: \(page.count) 条")
        // 处理数据...
    }
}
相关推荐
Yakamoz3 小时前
Swift Array的写时复制
swift
2501_915106324 小时前
HTTP 协议详解,HTTP 协议在真实运行环境中的表现差异
网络·网络协议·http·ios·小程序·uni-app·iphone
汉秋6 小时前
SwiftUI 中的 compositingGroup():真正含义与渲染原理
swiftui·swift
柯南二号7 小时前
【大前端】【iOS】iOS 真实项目可落地目录结构方案
前端·ios
2501_916007477 小时前
iOS与Android符号还原服务统一重构实践总结
android·ios·小程序·重构·uni-app·iphone·webview
汉秋7 小时前
SwiftUI 中的 @ViewBuilder 全面解析
swiftui·swift
二流小码农8 小时前
鸿蒙开发:自定义一个圆形动画菜单
android·ios·harmonyos
00后程序员张9 小时前
fastlane 结合 appuploader 命令行实现跨平台上传发布 iOS App
android·ios·小程序·https·uni-app·iphone·webview
2501_915106329 小时前
iOS 性能优化这件事,结合多工具分析运行期性能问题
android·ios·性能优化·小程序·uni-app·cocoa·iphone