You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Swift修改通用结构体代码导致构建缓慢,如何优化?

解决Swift Style结构体修改默认值导致全量编译的问题

我之前在大型Swift项目里也踩过这个坑,核心问题出在Swift结构体的默认属性值会成为公共接口的一部分,当你修改这些默认值时,编译器会认为结构体的初始化器接口发生了变化,进而触发所有引用该结构体的文件重新编译。下面是几个有效的解决方案,包括编译器配置和代码结构优化:

一、重构Style结构体,分离默认值与结构体定义

这是最有效的代码层面优化,能从根源上避免接口变化导致的全量编译:

把结构体的属性默认值移除,转而通过静态常量来定义预设样式:

struct Style {
    // 去掉属性的默认值,明确结构体的初始化需要传入所有参数
    let iconColor: UIColor
    let lightTextColor: UIColor
    
    // 预设样式作为静态常量,在这里定义默认值
    static let defaultStyle = Style(
        iconColor: .darkGray,
        lightTextColor: .gray
    )
    
    static let fancyStyle = Style(
        iconColor: .blue,
        lightTextColor: .green
    )
}

为什么这能解决问题?

原来的写法中,属性的默认值会让编译器自动生成带默认参数的初始化器(比如init(iconColor: UIColor = .darkGray, ...)),修改默认值等于修改了这个初始化器的公共接口,所有调用Style()或依赖该初始化器的代码都会被标记为需要重新编译。

而重构后,结构体的接口(属性类型、初始化器)完全固定,修改静态常量的赋值只是实现层面的变化,不会影响公共接口。此时编译器只会重新编译Style所在的文件,不会触发所有视图控制器的全量重建。

二、改用不可变类替代结构体

如果项目对值类型的需求不强烈,可以把Style改成不可变的类:

final class Style {
    let iconColor: UIColor
    let lightTextColor: UIColor
    
    init(iconColor: UIColor, lightTextColor: UIColor) {
        self.iconColor = iconColor
    }
    
    static let defaultStyle = Style(iconColor: .darkGray, lightTextColor: .gray)
    static let fancyStyle = Style(iconColor: .blue, lightTextColor: .green)
}

类是引用类型,修改静态常量defaultStyle的初始化参数时,只会编译Style类所在的文件,其他视图控制器因为只是持有Style的引用(接口未变化),不需要重新编译。这种方式的好处是不需要修改视图控制器中style属性的使用逻辑,只需要替换Style的定义即可。

三、编译器配置优化

除了代码重构,还可以通过Xcode的编译设置减少不必要的编译量:

  • 确保开启增量编译:在项目的Build Settings中,找到Build Options -> Incremental Build,设置为Yes(Xcode默认开启,但可以检查确认)。增量编译会只重新编译发生变化的文件,而不是整个模块。
  • 模块隔离:把Style结构体放到独立的Framework中。Swift的模块编译是独立的,修改Framework中的代码时,主项目只会重新编译直接引用该Framework中变化接口的文件,而不是全量编译主项目。不过这种方式的效果依赖于代码的耦合程度,如果大部分视图控制器都直接引用Style,还是会有一定的编译量,但比单一模块要好。

总结

最推荐的方案是重构Style结构体,分离默认值与结构体定义,既保留了值类型的优势,又从根源上避免了接口变化导致的全量编译。如果项目允许,改用不可变类也是一个简单有效的选项。

内容的提问来源于stack exchange,提问作者dr_barto

火山引擎 最新活动