Swift 5.3 新特性精讲(1):多尾闭包,一个不能自洽的特性

也许是作为争议最大的特性之一,多尾闭包这个特性被纳入 Swift 5.3。为什么会有那么大的争议呢?听我慢慢道来。

调用单个尾闭包的函数时有一种精简的写法:省去这个尾闭包的标签、并且把闭包放在圆括号外面。以下两种写法等价。

UIView.animate(withDuration: 0.25, animations: {
  // animation code
})

// 单个尾闭包的精简写法
UIView.animate(withDuration: 0.25) {
  // animation code
}

然而,Swift 5.3 之前如果有多个尾闭包的话,也只有最后一个闭包能被写成精简形式,这种写法苹果觉得不是太好看,因为一个闭包在圆括号内,另一个在外面。因此苹果的推荐做法是,碰到这种需要还是换成传统的写法吧。

// 写法 1(单尾闭包精简写法)
UIView.animate(withDuration: 0.25, animations: {
  // animation code
}) { completed in
  // completion code
}

// 写法 2(传统写法)
UIView.animate(withDuration: 0.25, animations: {
  // animation code
}, completion: { completed in
  // completion code
})

正因如此,给这次的争议埋下了隐患,因为这只是建议,并没有什么强制的措施,甚至编译器也不会给警告的。事实上,在 Xcode 12 之前,直接在最后一个闭包的代码提示上按回车,代码会被自动切换成第一种的形式。因此,第一种写法有非常多的存量代码。

Swift 5.3 的时候,苹果“重新思考”了多尾闭包的场景,它给出了一种新的调用写法:

// 写法 3(多尾闭包精简写法)
UIView.animate(withDuration: 0.25) {
  // animation code
} completion: { completed in
  // completion code
}

这种新的写法,把最后的连续几个闭包参数都算作是尾闭包了,这些闭包现在都可以放在圆括号外面了,显得清爽了很多。

但是,这些尾闭包中的第一个闭包被强制省略,可以看到这里的 animations标签被强制省略,注意是强制哦!加上了是编译不过的。苹果给出的解释是:如果允许第一个尾闭包加上标签,那么开发者需要考虑是加好还是不加好,你看这会导致代码风格不一致,那么干脆禁了吧。

呵呵。一个多尾闭包的情况现在都有三种合法的写法了,你不在考虑增加第三种写法的时候考虑以下是不是会让开发者多一种选择的为难,却贴心的考虑到了“我就是要”增加的第三种写法里面让开发者少一点为难,谢谢你啊。

这个多尾闭包还有个特性是,除了第一个尾闭包不配拥有姓名之外,其余的尾闭包都得有姓名(标签)。所以搞笑的是:同样作为合法的写法,第一种写法省略了completion标签,而第三种写法省略了animations标签。这不是找骂么,对于 API 的设计者来说,一个清晰合理的调用是函数设计考量的重要一点,之前我要想到的是多闭包的情况下最后一个标签有可能被用户调用时候省略,但是现在你说,不不,第一个才会被强制省略?

苹果怎么办呢?只好在 API 设计规范里面说,大家现在开始要注意第一个尾闭包标签会被省略,要给之后的尾闭包起个好的标签名哦。

然而,这又解决了什么问题呢?考虑到源代码的兼容性,Swift 永远不可能去掉第一种调用方法,用户依旧可以使用第一种写法来使用多尾闭包的函数,所以上面所述的问题也会永远存在。苹果可以做的不过是在今后新的多尾闭包写法铺开之后,悄咪咪地给第一种写法加一些 warning 和自动转换的功能吧。

引起那么大争论,我想苹果现在很后悔为什么当初就不禁止第一种写法。如果当初禁止第一种写法,现在的改动应该会是一片叫好吧。

那么又是什么原因让苹果不惜背上被开发者喷的代价也要把这个特性加上呢?

哎,不是因为 SwiftUI 又是为什么呢?

苹果铺垫了那么多,说是解决一个通用的多尾闭包的问题,最后才把真正的意图亮出来,为了 SwiftUI 的 API。

嘤嘤嘤,没有多尾闭包,人家只能把 Section<Parent, Content, Footer> 设计成这个样子啦!

init(header: Parent, footer: Footer, content: () -> Content)

// 调用
Section(
  header: ...,
  footer: ...
) {
  // content
}

这里只有 content 才用上了 ViewBuilder 修饰的闭包 ViewBuilder 介绍headerfooter只能传入实例,导致用户必须写一个新的类型才行。而理想情况下,这三个参数都应该是 ViewBuilder 修饰的闭包:

init(content: () -> Content, header:() -> Parent, footer: () -> Footer)

// 调用
Section {
  // content
} header: {
  ...
} footer: {
  ...
}

并且 content 标签是会被默认省略的,所以应当放到第一个。

我们看一个新的 SwiftUI 的组件再来体会下:

struct Gauge<Label, CurrentValueLabel, BoundsLabel, MarkedValueLabels> 
where Label : View, CurrentValueLabel : View, BoundsLabel : View, MarkedValueLabels : View

init<V>(value: V, in: ClosedRange<V>, 
label: () -> Label, 
currentValueLabel: () -> CurrentValueLabel, 
minimumValueLabel: () -> BoundsLabel, 
maximumValueLabel: () -> BoundsLabel)

终于可以撒开了欢地用尾闭包了呢!

结语

一门开源语言的进化动力,不能仅仅来源于一个私有框架。在去年为了 SwiftUI 引入大量新特性,并且最为关键的 FunctionBuilder 至今没有通过语言特性评审的情况下,苹果今年又一意孤行地引入多尾闭包这个争议特性,可谓是走了一条与开源精神背道而驰的路。

Swift 今年号称要推广到更多的平台,例如 Windows,这么维护开发者关系的话,很难说到时候有多少人会站在你这一边。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271