我不是罗大锤我不是罗大锤

我不是罗大锤

我不是罗大锤我不是罗大锤

我不是罗大锤

首页首页
分类分类
标签标签
友情链接友情
日记日记

在线人数:0 人

文章总浏览量:21158

Powered byNola

Swift Opaque 不透明类型Swift Opaque 不透明类型

Swift Opaque 不透明类型

&Swift#Swift

允许评论

6 个月前

一、Opeque 是什么

  • Protocol 前面加上 some 就建立了 Opaque 类型。
  • 主要用在函数的返回类型上。
    • 从 Swift 5.7 开始,some 的用途还可以用于:
      1. 方法参数 func process(_ value: some Equatable)
      2. 存储属性 var content: some Equatable
      3. 局部变量 let item: some Equatable = 42
      4. 下标 subscript(index: Int) -> some View
  • 把 Protocol 当类型用,并且确保只有一个具体类型,所以不用担心 Self 和 associatedtype 的问题。

下面有两个方法,展示了泛型和 Opeque。

func getLargerGeneric<T: Comparable>(_ a: T, _ b: T) -> T {
  a > b ? a : b
}

// 使用 some 关键字返回不透明类型
func getLargerOpaque<T: Comparable>(_ a: T, _ b: T) -> some Comparable {
  a > b ? a : b
}

// 类型:Int
let genericResult = getLargerGeneric(1, 5)

// 类型:some Comparable
let opaqueResult = getLargerOpaque(1, 5)
  • 在 protocol 前面加上 some,就建立了一个 Opaque 不透明类型。
  • Opaque 的类型一定是某“一个”类型。
  • 调用方拿到返回值,也只知道它是某一个固定的 Comparable 类型,无法知道具体的类型。

二、为什么需要 Opaque

  1. 隐藏复杂类型(以 Swift 为例):

    // 不使用 Opaque
    struct SwiftUIView: View {
      var body: Text {
            Text("Hello World")
      }
    }
    
    struct SwiftUIView: View {
      var body: VStack<TupleView<(Text, Image)>> {
        VStack {
          Text("Hello World")
          Image("person")
        }
      }
    }
    

    ​ 随着 UI 组件的增加,body 的返回类型会变得复杂,但对调用方来说只需知道它符合 View 协议。使用 Opaque 可以简化:

    // 使用 Opaque
    struct SwiftUIView: View {
      var body: some View {
        VStack {
          Text("Hello World")
          Image("person")
        }
      }
    }
    
  2. 性能优化:

    • some 在编译时确定具体类型,避免了 any 的动态派发开销。
    • 适合高频调用的函数或需要类型确定的场景(如 SwiftUI 的 body)
  3. 类型安全:

    • 通过 some 强制返回值的类型一致性:

      // ✅ 编译通过:两个分支返回相同类型(Int)
      func randomNumber() -> some Equatable {
          Bool.random() ? 42 : 100
      }
      
      // ❌ 编译错误:分支返回不同类型(Int 和 String)
      func randomValue() -> some Equatable {
          Bool.random() ? 42 : "42"
      }
      
  4. 封装灵活性:

    • 当需要更换返回类型时,不影响外部逻辑(外部只能操作协议定义的部分)

三、场景示例

1. 省略复杂类型

​ 下面展示了对一个范围数字进行的反转和切片操作,每一步操作返回的类型都会在上一层的类型上进行一层包装,导致返回类型变得复杂。使用 Opaque 可以隐藏这些无关类型:

// 类型:ClosedRange<Int>
let range = 1...1000					
// 类型:ReversedCollection<ClosedRange<Int>>
let reverse = range.reversed()	
// 类型:Slice<ReversedCollection<ClosedRange<Int>>>
let pre = reverse.prefix(5)			

// 不使用 Opaque,返回类型很复杂,并且这个实际类型对调用方来说没有任何意义
func suffix<T: Sequence>(_ sequence: T) -> Slice<ReversedCollection<T>> {
  sequence.reversed().prefix(5)
}

// 使用 Opaque,省略无关的类型,只需知道是一个 Sequence 即可
func suffix<T: Sequence>(_ sequence: T) -> some Sequence {
  sequence.reversed().prefix(5)
}

// 类型:some Sequence
let nums = suffix(1...1000)			

// 可以对 nums 调用 Sequence 的操作

2. 结合 Optional

Swift 5.7+ 开始支持

// 返回 (some Equatable)?
func maybeNumber() -> (some Equatable)? { 42 }

四、注意事项

  1. 类型一致性

    即使两个函数返回相同的逻辑类型,编译器也会视为不同:

    func a() -> some Equatable { 1 }
    func b() -> some Equatable { 1 }
    
    // ❌ 无法比较:a() 和 b() 的返回值被视为不同的不透明类型
    a() == b()
    
  2. 与泛型的区别

    • 泛型(如 func foo<T>() -> T)由调用方决定类型。
    • some 由函数内部决定类型。
  3. 协议关联类型:

    some 可以简化关联类型的处理:

    protocol Processor {
        associatedtype Output
        func process() -> Output
    }
    
    // 使用 some 隐藏关联类型
    func createProcessor() -> some Processor {
        struct IntProcessor: Processor {
            typealias Output = Int
            func process() -> Int { 42 }
        }
        return IntProcessor()
    }
    
目录
一、Opeque 是什么
二、为什么需要 Opaque
三、场景示例
1. 省略复杂类型
2. 结合 Optional
四、注意事项
暂无评论