懒加载组件书写的困恼
我写UI组件,特别喜欢使用全局下面的懒加载,大概形式如下:
swift
class ViewController: UIViewController {
private lazy var label: UILabel = {
let label = UILabel()
label.text = "season"
return label
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(label)
}
}
我这么写有几个原因:
- 组件只有在确实被调用的时候才会被调用了;
- 将其设为全局,如果后续需要对赋值或者更新,信手捏来
但是!但是这么写非常的繁琐:
我需要声明类型: UILabel
,同时在大括号里面初始化一个label并且return label!想想我都觉得自己不停的废话,如果一页有非常多UI组件,实在很遭罪!
有人会说,你可以用AI呀,GitHub Copilot for Xcode,虽然它会联系上下文,可以让我一路tab,但是这玩意有延迟,同时它也是收费的,白嫖完次数就没补全了。
直到我使用了Then
,瞬间被其语法糖折服,这是使用Then
库之后的代码:
swift
class ViewController: UIViewController {
private lazy var label = UILabel().then { $0.text = "season" }
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(label)
}
}
Then
是一个非常简洁实用的 Swift 框架,主要用于让对象的初始化和属性配置更加优雅和简洁。它通过扩展 Swift 的类型,提供了链式语法,让你可以在创建对象时直接配置属性,提升代码可读性和开发效率。
1. 基本用法
示例:
swift
import Then
let label = UILabel().then {
$0.text = "Hello, Then!"
$0.textColor = .red
$0.textAlignment = .center
}
private lazy var button = UIButton(type: .custom).then {
$0.setTitle("你打我撒~", for: .normal)
$0.setTitleColor(.systemBlue, for: .normal)
$0.setImage(UIImage(named: "小剑剑"), for: .normal)
}
这样你就可以在初始化对象的同时,直接配置它的属性,避免多次写变量名。
2. 支持的类型
Then 支持所有继承自 NSObject
的类(如 UIKit 组件),以及结构体(如 CGRect、CGSize、UIEdgeInsets 等)。
结构体用法:
swift
let rect = CGRect().with {
$0.origin.x = 10
$0.size.width = 100
}
3. 源码解析
Then 的核心实现其实非常简单,主要是通过 Swift 的协议扩展:
swift
public protocol Then {}
extension Then {
@inlinable public func with(_ block: (inout Self) throws -> Void) rethrows -> Self
@inlinable public func `do`(_ block: (Self) throws -> Void) rethrows
}
extension Then where Self : AnyObject {
@inlinable public func then(_ block: (Self) throws -> Void) rethrows -> Self
}
extension NSObject: Then {}
#if !os(Linux)
extension CGPoint: Then {}
extension CGRect: Then {}
extension CGSize: Then {}
extension CGVector: Then {}
#endif
extension Array: Then {}
extension Dictionary: Then {}
extension Set: Then {}
extension JSONDecoder: Then {}
extension JSONEncoder: Then {}
#if os(iOS) || os(tvOS)
extension UIEdgeInsets: Then {}
extension UIOffset: Then {}
extension UIRectEdge: Then {}
#endif
- 只要你的类型是
NSObject
的子类,就自动拥有了then
方法。 - 结构体则通过
with
实现类似功能。 do
即可以在类里面使用,也可以在结构体中使用。- 库对Swift内置的一些结构体类型做了扩展,以方便使用。
这里把@inlinable单独拿出来说一下,我以前也见的多,也知道和性能有关系,不过确实是那么了解。
@inlinable的作用
@inlinable
是 Swift 的一个函数属性修饰符,主要作用如下:
作用
- 允许函数体在模块外可见
被@inlinable
修饰的方法或属性,其实现代码会暴露给其他模块(即使是internal
访问级别),这样编译器可以在调用处直接内联优化,提高性能。 - 提升性能
编译器可以将这些方法的调用"内联",减少函数调用开销,尤其适用于小型、频繁调用的函数。
典型使用场景
- 框架或库中希望对外暴露实现细节、让调用方获得更好性能的公共 API。
- 例如 Swift 标准库中很多常用方法都用
@inlinable
修饰。
注意事项
- 滥用可能导致编译体积变大,因为实现代码会被复制到每个调用点。
- 只建议用于小型、性能敏感的函数。
小结:
@inlinable
让方法实现对外可见,便于跨模块内联优化,常用于 Swift 框架和库的高性能 API。
4.then
、with
、do
的用法解析
①. then
swift
let label = UILabel().then {
$0.text = "Hello"
$0.textColor = .red
}
②. with
- 适用对象 :结构体(
struct
),如CGRect
、CGSize
等。 - 作用:复制结构体,配置属性后返回新的结构体实例(因为结构体是值类型,不能直接修改原对象)。
- 常见用法:
swift
let rect = CGRect().with {
$0.origin.x = 10
$0.size.width = 100
}
// rect 是配置后的新实例
③. do
- 适用对象:类和结构体都可以。
- 作用 :对对象执行一些操作,但不返回对象本身,通常用于执行副作用(如调用方法、打印等)。
- 常见用法:
swift
UserDefaults.standard.do {
$0.set("value", forKey: "key")
$0.synchronize()
}
// 仅执行 block 内操作,不返回对象
总结对比
方法 | 适用类型 | 是否返回对象 | 主要用途 |
---|---|---|---|
then | 类 | 是 | 初始化并配置属性 |
with | 结构体 | 是 | 配置属性并返回新实例 |
do | 类/结构体 | 否 | 执行副作用操作 |
一句话记忆:
then
用于类的链式属性配置,with
用于结构体的链式属性配置,do
用于执行操作但不返回对象。
5. 优势总结
- 链式语法,代码更简洁
- 初始化和配置合二为一
- 适用于类和结构体
- 虽然对业务代码无侵入性,易于集成,但是,一旦使用了Then,代码里面都使用了这种书写格式,想要去掉Then库就特别困难了
6. 典型场景
- UI 组件的属性配置
- Model 对象的初始化
- 结构体的便捷赋值
结论:
Then 是一个轻量级、无侵入的 Swift 语法糖工具,极大提升了代码的可读性和开发效率,推荐在日常开发中使用。
同时Then更难得可贵的是其面向协议编程的思想,通过协议下扩展的方法,最后让需要使用的类型去遵守协议,以达到四两拨千斤的目的。
Swift面向协议的思想,在很多场景下都有非常不错的效果,值得我们反复揣摩与学习。
我最早接触then这个关键词是在JavaScript中的网络请求中的异步回调中,then里面是网络请求的响应,虽然Swift中并不完全是这个意思,不过还是非常便于理解的。