ResultBuilder 学习笔记(一)

ResultBuilder 学习笔记(一)

ResultBuilder 是Swift 语言的一个非常重要、强大特性,允许开发者以声明方式实现简洁、清晰、优雅的代码。在 Swift 5.4 之前,它被称为@functionBuilder,之后被正式更名为@resultBuilder

使用ResultBuilder技术可以在Swift 中非常容易创建领域特定语言( DSL)。DSL 允许开发人员使用更自然且特定于领域的语法来执行特定任务,例如 SwiftUI 中的 UI 组件构建,从而使代码更易于编写、阅读和维护。

估计许多初学者也和作者开初一样,看了上面描述,对ResultBuilder是什么还是一头雾水。别急,下面我们通过一个简单示例来一一说明。 我们的例子很简单,字符串拼接,将个数不定的字符串拼接起来,形成一个新的字符串。我们比较一下,采用和不采用ResultBuilder这两种情形下有什么差别。

先不采用ResultBuilder。如下函数实现了上述拼接功能:

swift 复制代码
func concat(_ components: String...) ->String
{
    return components.joined(separator: " ")
}

代码非常简单。如下是使用该函数:

swift 复制代码
let str = concat("春眠不觉晓","处处闻啼鸟", "夜来风雨声","花落知多少") 
print(str)

上述代码其实没有什么问题。但是,如果采用下面的ResultBuilder方式是不是更好一些呢?

swift 复制代码
let str = concatBulder {
"春眠不觉晓"
"处处闻啼鸟"
"夜来风雨声"
"花落知多少" 
}
print(str)

与前一种方式比较,后一种方式显然更简洁,自然,你也许会说,把第一种方式换成多行书写,不是一样吗?

swift 复制代码
let str = concat (
  "春眠不觉晓",
  "处处闻啼鸟",
  "夜来风雨声",
  "花落知多少"
) 
print(str)

还真的不一样。首先逗号是多余的,不简洁。其次是小括号,将小括号的内容分解到多行不是很好的实践。

将大括号内容写在一行上也不是好的实践。

如果我们考虑给拼接功能增加一个分隔符,会是怎么样的呢?

第一种方式是这样的:

swift 复制代码
let str = concat (
  "-",
  "春眠不觉晓",
  "处处闻啼鸟",
  "夜来风雨声",
  "花落知多少"
) 
print(str)

显然,问题更严重了。分隔符和正常字符串容易混淆,给使用带来困扰。 加一个参数名separator又如何呢,如下:

swift 复制代码
let str = concat (
  separator:"-",
  "春眠不觉晓",
  "处处闻啼鸟",
  "夜来风雨声",
  "花落知多少"
) 
print(str)

这下,分隔符似乎清楚了,但感觉还是怪怪的,总之还是别扭。

而采用ResultBuilder方式是这样的:

swift 复制代码
let str = concatBuilder("-") { 
  "春眠不觉晓"
  "处处闻啼鸟"
  "夜来风雨声"
  "花落知多少"
}
print(str)

可以看到后者的代码仍然保持简洁、清晰,优雅。

通过上面比较可以看到,即便对这样一个简单的问题,如果稍稍扩展一下(需求),无论如何调整,用普通方式都容易产生不太好的代码。解决办法就是ResultBuilder,它的一个最大的优势就是有助于开发者写出简洁、清晰、优雅的代码。

如何实现

ResultBuilder 技术并不是一组协议,而是一组静态方法。 根据应用需求,我们必须实现其中的一个或者多个方法,而且,必须至少有一个buildBlock()方法。

静态方法 简要说明
buildBlock(...) 构建顺序语句块
buildOptional (...) 构建没有 else 的 if 语句
buildEither(first: )和buildEither(second: ) 构建if-else 语句
buildArray (...) 构建循环语句
buildExpression (...) 构建表达式语句
buildFinalResult (...) 将临时内部类型转换为最终外部类型

通常,我们总是在枚举或者结构体中实现相应的方法。对我们的字符串拼接需求,目前我们只需要实现一个buildBlock()方法即可。

如下是实现代码:

swift 复制代码
@resultBuilder
struct ConcatBuilder {
    static func buildBlock(_ components: String...) -> String {
        return components.joined(separator: "")
    }
}

完毕,就这么简单,我们实现了字符串拼接功能的ResultBuilder版本,它和我们常见的ViewBuilder完全类似,当然啰,功能比后者简单许多。 用上述 ConcatBuilder 改造 concat() 函数, 新函数命名为concatBuilder()。如下:

swift 复制代码
func concatBuilder(@ConcatBuilder _ builder: () -> String) -> String {
    return builder()
}

通过前面的对比,我们已经了解,采用ResultBuilder技术的 concatBuilder 已经比concat()函数优越很多,但它仍然非常简单,我们的拼接需求会变化,我们需要更复杂的功能,比如,我们需要支持if-else语句,或者循环功能。如下所示:

swift 复制代码
var include = true 
let str = concatBuilder { 
  "春眠不觉晓"
   
   for i in 1...5
   {
     "处处闻啼鸟"
   }
   if include 
   { 
   "夜来风雨声" 
   }
   else
   {
     "花落知多少"
   }
}

这些都可以通过在ConcatBuilder结构体中实现更多的的buildXXX()方法来实现。在后续博文中,作者会通过更多示例,介绍如何用ResultBuilder实现上述功能。敬请关注。

相关推荐
良技漫谈1 天前
Rust移动开发:Rust在iOS端集成使用介绍
后端·程序人生·ios·rust·objective-c·swift
KeithTsui2 天前
ZFC in LEAN 之 前集的等价关系(Equivalence on Pre-set)详解
开发语言·其他·算法·binder·swift
袁代码2 天前
Swift 开发教程系列 - 第4章:函数与闭包
ios·swift·ios开发
安泽13143 天前
高德地图美食
开发语言·swift·美食
袁代码3 天前
Swift 开发教程系列 - 第2章:Swift 基础语法
swift·ios开发·基础教程
袁代码3 天前
Swift 开发教程系列 - 第1章:Swift 简介与开发环境配置
swift·ios开发·基础教程
孚亭4 天前
一些swift问题
swift
莫问alicia4 天前
echarts 实现3D饼状图 加 label标签显示
前端·3d·echarts·swift
uiop_uiop_uiop6 天前
iOS Swift5算法恢复——HMAC
ios·iphone·swift
東三城7 天前
【ios】---SwiftUI开发从入门到放弃
ios·swiftui·swift·1024程序员节