Swift和SwiftUI中的`some`关键字:它为什么重要?

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

神秘的“some”:为什么成千上万的开发者都在问它?

\n\n

如果你曾经用SwiftUI写过代码,那么你一定见过some View这种结构。它看起来很简单,但新手常常感到困惑:“这个‘some’是什么?为什么不能直接写View?如果我删掉some,代码会出错吗?” 问题“What is the "some" keyword in Swift(UI)?”在Stack Overflow上获得了数千次浏览——这并非偶然。这个简短的单词背后,隐藏着现代Swift的一整套哲学。

\n\n

在本文中,我们将深入探讨关键字some是什么,它为什么出现在这门语言中,以及为什么没有它就无法想象SwiftUI。你将了解它如何与泛型、协议和“不透明类型”(opaque types)相关联。更重要的是,你将学会如何编写更简洁、性能更高的代码。

\n\n

“some”要解决的问题

\n\n

想象一下,你正在编写一个函数,该函数返回一个符合View协议的对象。在SwiftUI中,这种情况经常发生:

\n\n
func makeGreeting() -> View {\n    return Text("Hello!")\n}
\n\n

Swift编译器会提示:“协议'View'只能用作泛型约束(generic constraint),因为它有Self或关联类型要求”。听起来很吓人,但本质很简单:SwiftUI中的View协议要求在编译时知道具体类型。如果我们返回View——这是一个抽象概念,而编译器需要具体信息。

\n\n

以前,开发者通过泛型来绕过这个问题:

\n\n
func makeGreeting<T: View>() -> T {\n    return Text("Hello!") as! T\n}
\n\n

这虽然能工作,但看起来像是一个权宜之计:你向编译器承诺返回T,但实际上是在“假装”。此外,调用代码必须显式指定类型。既不优雅也不安全。

\n\n

“Some”——模板代码的救星

\n\n

Swift中的关键字some出现在5.1版本中,正是为了应对这种情况。它告诉编译器:“我将返回一个符合协议的具体类型,但我不想说出它的名字。你在编译时自行确定它。” 这被称为不透明类型(opaque type)。

\n\n

回到我们的例子:

\n\n
func makeGreeting() -> some View {\n    return Text("Hello!")\n}
\n\n

现在代码可以编译了!编译器知道返回的正是Text,但从外部我们只能看到some View。这带来了两个好处:

\n
    \n
  • 类型安全——编译器检查返回的类型始终是同一个。
  • \n
  • 封装性——我们对调用代码隐藏了实现细节。
  • \n
\n\n

比较:“some” vs 泛型 vs 协议

\n\n

为了更好地理解some的魔力,让我们通过一个具体例子来比较这三种方法。假设我们想编写一个返回图形(圆形或正方形)的函数。

\n\n

1. 返回协议(无法编译)

\n\n
protocol Shape {\n    func draw()\n}\n\nstruct Circle: Shape {\n    func draw() { print("绘制圆形") }\n}\nstruct Square: Shape {\n    func draw() { print("绘制正方形") }\n}\n\n// 错误:协议'Shape'不能用作返回类型\nfunc makeShape() -> Shape {\n    return Circle()\n}
\n\n

正如我们所知——如果协议有关联类型或Self要求,这种方式行不通。

\n\n

2. 使用泛型

\n\n
func makeShape<T: Shape>() -> T {\n    // 需要以某种方式创建T,但我们不知道具体类型\n    fatalError("无法创建泛型")\n}
\n\n

问题:调用代码必须知道期望什么类型。函数本身无法决定返回什么。

\n\n

3. 使用“some”(完美工作)

\n\n
func makeShape() -> some Shape {\n                

博客

书籍推荐