「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战」。
DSL
DSL(Domain Specific Language) 翻译成中文就是:“领域特定语言”。首先从定义就可以看出,DSL 也是一种编程语言,只不过它主要是用来处理某个特定领域的问题。那么我们在学习iOS最先接触DSL是在哪里呢?我觉得是在CocoaPods里,但是那时候的状态就像他认识了我,而我不认识他(关于CocoaPods dsl 可以看这篇文章)。当我开始认识他的时候是在Masonry这个布局库中。在Masonry的介绍里面有这样一段话:
Masonry is a light-weight layout framework which wraps AutoLayout with a nicer syntax. Masonry has its own layout DSL which provides a chainable way of describing your NSLayoutConstraints which results in layout code that is more concise and readable. Masonry supports iOS and Mac OS X.
从这里我看见了DSL一词去了解他,也去学习了他。并且在wwdc2021Write a DSL in Swift using result builders介绍了通过在 Swift使用结果构建器编写DSL以及讲解了DSL,建议大家看一下。
NSAttributedString DSL
上篇我们已经写了在构建Layout DSL,那么这篇我们尝试构NSMutableAttributedString DSL。首先我们写一个NSAttributedString扩展来封装:
public extension NSAttributedString {
/// Sets the color of this text
@discardableResult
func foregroundColor(_ color: UIColor) -> NSAttributedString {
let mutableAtt = NSMutableAttributedString(string: self.string, attributes: self.attributes(at: 0, effectiveRange: nil))
mutableAtt.addAttributes(attributes, range: NSMakeRange(0, (self.string as NSString).length))
return mutableAtt
}
.....
}
//这里我们发现其他的属性background、underline等等都是用同样的代码封装,所以我们进行提取封装成如下:
public extension NSAttributedString {
/// Sets the color of this text
@discardableResult
func foregroundColor(_ color: UIColor) -> NSAttributedString {
self.addStyle([.foregroundColor : color])
}
.....
func addStyle(_ attributes: [NSAttributedString.Key:Any]) -> NSAttributedString {
let mutable = NSMutableAttributedString(string: self.string, attributes: self.attributes(at: 0, effectiveRange: nil))
mutable.addAttributes(attributes, range: NSMakeRange(0, (self.string as NSString).length))
return mutable
}
}
这样我们在使用的时候这样写:
let att1 = NSMutableAttributedString(string: "Hello ")
att1.foregroundColor(.blue).background(.orange)
let att2 = NSAttributedString(string: "World")
att2.foregroundColor(.blue).background(.orange)
这样我们实现了单个 NSAttributedString实现了链式调用。但是如果多个NSAttributedString 合并:
att1.append(att2) //后面不停的append
所以这里我们可以进行合并封装,不过这里要用到Swift 一个特性:Function Builder ,他会很大程度增强了 Swift 语言构建内置 DSL 的能力。具体关于Function Builder的介绍,可以看这篇文章。这里介绍下面用到的俩个:
-
static func buildBlock(_ components: Component...) -> Component将可变数量的结果合并为一结果。 -
static func buildOptional(_ component: Component?) -> Component将可选的中间结果构造成新的中间结果。用于支持不包含else闭包的if表达式。
@resultBuilder
public struct AttributedStringBuilder {
@discardableResult
public static func buildBlock(_ components: NSAttributedString...) -> NSAttributedString {
let string = NSMutableAttributedString()
components.forEach { string.append($0) }
return string
}
@discardableResult
public static func buildOptional(_ component: NSAttributedString?) -> NSAttributedString {
guard let component = component else { return component }
return component
}
}
public extension NSAttributedString {
@discardableResult
convenience init(@AttributedStringBuilder _ content: () -> NSAttributedString) {
self.init(attributedString: content())
}
}
这里多说一句在swift 对于Result Builders进行了升级,具体可以看result builders。同时对于swift 版本升级的变化,可以看这篇文章
这样我们就能将多个NSAttributedString 合并了:
NSAttributedString {
att1
att2
}
当然,我们还可以给字符串添加扩展,来省略这样一句话:
let att1 = NSMutableAttributedString(string: "Hello ")
public extension String {
/// Sets the color of this text
func foregroundColor(_ color: UIColor) -> NSAttributedString {
NSAttributedString(string: self, attributes: [.foregroundColor : color])
}
.......
/// Returns this string as an `NSAttributedString`
var attributed: NSAttributedString {
NSAttributedString(string: self)
}
}
我们就可以这样书写了:
NSAttributedString {
"Hello ".foregroundColor(.blue)
"World".foregroundColor(.blue)
}
到此关于DSL的介绍就结束了。