PPPan's 平凡之路

做一个互联网内容的贡献者

PPPan's 平凡之路 一个技术博客覆盖范围包括 iOS Objecticve-C Swift Xcode 等


从 Swift 中的 max(_:_:) 看设计哲学

由于 Swift 不再支持宏了,于是 MAX,MIN 等一些列常用宏都被重写为 Swift 函数。我们来看看函数的定义:

1
2
@warn_unused_result
public func max<T : Comparable>(x: T, _ y: T) -> T

warn_unused_result

先说说 @warn_unused_result 注解。 顾名思义,被该注解标记的方法所产生的返回值,如果未被使用,编译器会不开心哦(编译的时候会产生一条警告⚠️)。

这里讲一下方法的设计哲学。一般来说一个理想的带有返回值的方法,除了返回返回值以外,是不会产生任何其他副作用的。比如说不会改变入参的值(这也是 inout存在的原因)
,不会对系统的其他状态有影响(比如说不会改变类的实例变量的值)。这样的好处是减少方法产生的不确定性,同时方法使用的目的变得非常明确。

max 来举例。max 方法被设计用来判断并返回两个参数中更大的那个。容易理解,当返回值没有被使用的时候,这次方法的调用也是没有意义的,因此 @warn_unused_result 的标注是非常合理的。

所以当你在开发 SDK 或是框架,甚至是和同事合作开发业务,同事会用到你的 API 的时候,你可以在需要的地方,用此注解来标记你的方法,以提示使用者注意使用方法产生的返回值。

面向协议

从方法的定义中,我们可以看到,传入的参数只要符合 Comparable 协议,就可以正确得到返回结果。

这意味着我们不仅可以用它来比较数字大小:

1
let maxInteger = max(10, 20)

还可以用来比较 String 的大小

1
let maxString = max("bc", "abc")

甚至是自定义的对象,只要它实现了 Comparable 协议。从这一点上,我们可以窥见 Swift “面向协议编程” 的一斑。面向协议编程让 max(_:_:) 从具体的类中解放出来,不关心入参是什么类,有什么结构,只关心 所传进来的参数能否被比较,而具体的比较则让入参自己去做,从而让 max(_:_:) 具有普适性,达到程序设计上的低耦合。

这么说可能有点抽象,我们来举个具体的例子。

假设我们有这样的餐馆定义

1
2
3
4
5
6
7
struct Restaurant {
var area: Int // 占地面积
var michelinRate: Int // 米其林等级
var averageCoast: Int // 人均消费
var name: String // 餐厅名称
var logo: UIImage // 餐厅LOGO
}

如何比较两家餐馆谁大谁小?

按照餐馆面积比,平海路上的日料店表示不服;按照人均消费比,新白鹿表示不服;按照米其林等级比,一众特色小吃店表示不服;按照餐厅名称长度比,产品经理表示不服。

总地来说,”谁更大”其实是一种抽象的概念, max 方法不能也不该知道入参是如何比较的。只有入参自己知道自己该如何比较。因此抽象出 Comparable 协议是非常自然的。

Comparable

我们再来看看 Comparable 的定义

1
2
3
4
5
6
7
8
9
10
11
public protocol Comparable : Equatable {
/// over instances of `Self`.
@warn_unused_result
public func <(lhs: Self, rhs: Self) -> Bool
@warn_unused_result
public func <=(lhs: Self, rhs: Self) -> Bool
@warn_unused_result
public func >=(lhs: Self, rhs: Self) -> Bool
@warn_unused_result
public func >(lhs: Self, rhs: Self) -> Bool
}

Comparable 抽象了数学上严格全序的概念。虽然定义了五个方法(别忘了 Equatable 中还有一个 ==),但只需要实现 ==< ,其他方法只需要使用 Swift 标准库提供的默认实现,就可以根据严格全序定义推导出实现。

数学真是计算机科学的基石啊!

Newer Post

深挖 Swift 中的字符串可比性

在写 《从 Swift 中的 max(_:_:) 看设计哲学》的过程中,产生了这样一个想法: 既然 max(_:_:) 可以用来对比任何 Comparable ,而 String 是符合 Comparable 的,那么用它来比较两个字符串”谁大谁小”会怎么样呢? 由于实验结果的捉摸不定,于是产生了 …

继续阅读
Older Post

iOS 持续集成系列 - 开篇

iOS 持续集成 - 开篇 iOS 持续集成 - 自动化 Code Review [iOS 持续集成 - 自动化单元测试] [iOS 持续集成 - 自动化打包与分发] 前言iOS 开发在经过这几年的野蛮生长之后,慢慢地趋于稳定。无论开发语言是 Objective-C 还是 Swift,工程类型是 …

继续阅读