@resultBuilder 结果构造器

定义

结果构造器: 以声明式的语法构建数组等嵌套式的数据。

优点

  1. 使代码的结构清晰,提高可读性。
  2. 减少了临时变量的使用

举例

实现设置菜单列表:

import Foundation

struct Setting {
    enum Value {
        case int(Int)
        case bool(Bool)
        case group([Setting])
    }
    var name: String
    var value: Value
}

@resultBuilder
struct SettingsBuilder {
    static func buildBlock(_ settings: Setting...) -> [Setting] { settings  }
}

@SettingsBuilder func getSettings() -> [Setting] {
    Setting(name: "option 1", value: .int(1))
    Setting(name: "option 2", value: .bool(true))
    Setting(name: "option 3", value: .group([
        Setting(name: "option 3.1", value: .bool(true)),
        Setting(name: "option 3.2", value: .bool(true))
    ]))
}

let settings = getSettings()
print(settings)

Result Builder的使用有两个基本原则:

  1. 必须使用@resultBuilder修饰符修饰
  2. 必须至少实现一个buildBlock静态方法

进阶

支持if语句

import Foundation

protocol SettingsConvertible {
    func asSettings() -> [Setting]
}

struct Setting: SettingsConvertible {
    enum Value {
        case int(Int)
        case bool(Bool)
        case group([Setting])
    }
    
    var name: String
    var value: Value
    
    func asSettings() -> [Setting] { [self] }
}

extension Array: SettingsConvertible where Element == Setting {
    func asSettings() -> [Setting] { self }
}

@resultBuilder
struct SettingsBuilder {
    static func buildBlock(_ component: SettingsConvertible...) -> [Setting] {
        return component.flatMap{ $0.asSettings() }
    }
    static func buildOptional(_ component: SettingsConvertible?) -> SettingsConvertible {
        component ?? []
    }
}
let flag1 = false
@SettingsBuilder func getSettings() -> [Setting] {
    Setting(name: "option 1", value: .int(1))
    if flag1 {
        Setting(name: "option 2", value: .bool(true))
    }
    Setting(name: "option 3", value: .bool(true))
}

let settings = getSettings()
print(settings)

原理伪代码:

let s1 = Setting(name: "option 1", value: .int(1))
let if_1: Setting? = nil
if flag {
	let s2 = Setting(name: "option 2", value: .bool(true))
	if_1 = SettingsBuilder.buildBlock(s2)
}
let r_1 = SettingsBuilder.buildOptional(if_1)
let s3 = Setting(name: "option 3", value: .bool(true))
let result = SettingsBuilder.buildBlock(s1, r_1, s3)

从上面可以看出:

  1. 构造总是从上往下执行的
  2. if分支内部会单独执行一次buildBlock,并将buildBlock的结果作为入参传给buildOptional
  3. 最后会将普通构造结果和buildOptional的输出一起作为入参调用buildBlock
  4. buildBlock的出参会作为buildOptional的入参,buildOptional的出参又会作为buildBlock的入参,因此引入了协议SettingsConvertible

支持if/else语句或switch/case语句

需要实现buildEither(first:)和buildEither(second:)方法

import Foundation

protocol SettingsConvertible {
    func asSettings() -> [Setting]
}

struct Setting: SettingsConvertible {
    enum Value {
        case int(Int)
        case bool(Bool)
        case group([Setting])
    }
    
    var name: String
    var value: Value
    
    func asSettings() -> [Setting] { [self] }
}

extension Array: SettingsConvertible where Element == Setting {
    func asSettings() -> [Setting] { self }
}

@resultBuilder
struct SettingsBuilder {
    static func buildBlock(_ component: SettingsConvertible...) -> [Setting] {
        return component.flatMap{ $0.asSettings() }
    }
    
    static func buildEither(first component: SettingsConvertible) -> SettingsConvertible {
        return component
    }
    
    static func buildEither(second component: SettingsConvertible) -> SettingsConvertible {
        return component
    }
}

let flag1 = false

@SettingsBuilder func getSettings() -> [Setting] {
    Setting(name: "option 1", value: .int(1))
    if flag1 {
        Setting(name: "option 2", value: .bool(true))
    } else {
        Setting(name: "option 3", value: .bool(true))
    }
    
    Setting(name: "option 4", value: .bool(true))
}

let settings = getSettings()
print(settings)

在闭包中使用

import Foundation

protocol SettingsConvertible {
    func asSettings() -> [Setting]
}

struct Setting: SettingsConvertible {
    enum Value {
        case int(Int)
        case bool(Bool)
        case group([Setting])
    }
    
    var name: String
    var value: Value
    
    func asSettings() -> [Setting] { [self] }
}

struct SettingGroup: SettingsConvertible {
    var name: String
    var settings: [Setting]
    
    init(name: String, @SettingsBuilder builder: () -> [Setting]) {
        self.name = name
        self.settings = builder()
    }
    
    func asSettings() -> [Setting] {
        [Setting(name: name, value: .group(settings))]
    }
}

@resultBuilder
struct SettingsBuilder {
    static func buildBlock(_ component: SettingsConvertible...) -> [Setting] {
        return component.flatMap{ $0.asSettings() }
    }
}

@SettingsBuilder func getSettings() -> [Setting] {
    Setting(name: "option 1", value: .int(1))
    Setting(name: "option 2", value: .bool(true))
    SettingGroup(name: "option 3") {
        Setting(name: "option 3.1", value: .bool(true))
        Setting(name: "option 3.2", value: .bool(true))
    }
}

let settings = getSettings()
print(settings)

在for…in语句中使用

import Foundation

protocol SettingsConvertible {
    func asSettings() -> [Setting]
}

struct Setting: SettingsConvertible {
    enum Value {
        case int(Int)
        case bool(Bool)
        case group([Setting])
    }
    
    var name: String
    var value: Value
    
    func asSettings() -> [Setting] { [self] }
}

extension Array: SettingsConvertible where Element == Setting {
    func asSettings() -> [Setting] { self }
}

@resultBuilder
struct SettingsBuilder {
    static func buildBlock(_ components: SettingsConvertible...) -> [Setting] {
        components.flatMap{ $0.asSettings() }
    }
    
    static func buildArray(_ components: [[Setting]]) -> [Setting] {
        components.flatMap { $0 }
    }
}

@SettingsBuilder func getSettings() -> [Setting] {
    for index in 1...3 {
        Setting(name: "option \(index)", value: .bool(true))
    }
}

let settings = getSettings()
print(settings)

原理伪代码:

var array = [[Setting]]()
for index in 1...3 {
    let s = Setting(name: "option \(index)", value: .bool(true))
    let s_1 = SettingsBuilder.buildBlock(s)
    array.append(s_1)
}
let r1 = SettingsBuilder.buildArray(array)
let result = SettingsBuilder.buildBlock(r1)

从上面可以看出:

  • 构造总是从上往下执行的
  • for内部会单独执行一次buildBlock,并将buildBlock的多次执行的结果合成一个数组
  • 最后会将上述数组作为入参调用buildArray
  • 最后会将普通构造结果和buildArray的输出一起作为入参调用buildBlock
  • buildBlock的出参合成一个数组作为buildArray的入参,buildArray的出参又会作为buildBlock的入参

应用场景

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值