一、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)}
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) 条")
// 处理数据...
}
}