iOS 中资源的条件编译
日常开发中常常遇到某些类及其引用的资源文件仅在 Debug
或内测版本中生效,而不希望带到线上版本。因为一来会增加包体积,二来会把一些内部功能的相关接口暴露,导致可能的一些动态调试。那么有没有避免的方案呢?
Pod
答案肯定是有的,通常的做法是把对应功能抽成单独的 pod
库,然后仅 Debug
模式才集成到主工程,发出去的版本不集成。通常方式如下,以 FLEX
为例:
pod 'FLEX', '~> 2.0', :configurations => ['Debug']
但是如果某些强耦合主工程无法拆成独立 pod
的功能,这种方式可能就不再适用了。
条件编译
pod
这条路走不通,有没有其他方式呢?答案也是肯定的,也是大家经常采用的方式,使用条件编译。也就是将对应类文件及引用到的地方使用类似下面的方式:
#if DEBUG
// 放入仅Debug模式下生效的代码
#endif
以上两种方式将大部分场景 cover
住了,但是假设对应的类文件还引用到了图片、Storyboard
、Xib
等资源时,该如何让这些资源仅在 Debug
下才参与编译,而其他场景不参与编译呢?
Xcode Build Options
既然都写了这篇文章了,那答案也是肯定的,就是使用 Xcode
提供的一个编译选项 Excluded Source File Names
来将不需要的资源排除。
![](/media/2020-03-30/15855806858197.jpg)
确切来说这个编译选项是
Xcode 9
才引入的,之前版本需要自行定义,可以参考这个回答。 摘录一段来自Xcode Build Settings的解释
假设有下面目录结构的工程
# 使用 tree 命令行得到下面结构
├── ConditionBuildDemo
│ ├── A
│ │ ├── Debug
│ │ │ ├── DebugA.swift
│ │ │ ├── icon_test@2x.png
│ │ │ ├── Sub
│ │ │ │ ├── DeepSub
│ │ │ │ │ └── DeepSubA.swift
│ │ │ │ ├── SubDebugA.swift
│ │ │ │ └── icon.png
│ │ │ └── SubA.xcassets
│ │ │ ├── Contents.json
│ │ │ └── logo.imageset
│ │ │ ├── Contents.json
│ │ │ └── logo@2x.png
│ │ └── ModelA.swift
│ ├── B
│ │ ├── Debug
│ │ │ └── DebugB.swift
│ │ └── ModelB.swift
//测试代码
override func viewDidLoad() {
super.viewDidLoad()
let _ = ModelA()
let _ = ModelB()
let _ = DebugA()
let _ = DebugB()
let _ = SubDebugA()
let _ = DeepSubA()
if let _ = UIImage.init(named: "logo") {
print("has logo")
}
if let _ = UIImage.init(named: "icon") {
print("has icon")
}
if let _ = UIImage.init(named: "icon_test") {
print("has icon_test")
}
}
这里为了方便验证,直接设置
Debug Configuration
下的Excluded Source File Names
。如图实际用到项目中可根据自己项目自行对不同
Configuratio
进行配置。
下面我们来探索下这个路径的配置规则。
如果我们想要排除 A
和 B
目录下的 Debug
文件夹的所有内容,按照常规思路,理所当然直接写上 Debug/*
,然后编译发现上面的测试代码编译报错,找不到类 DebugA
,DebugB
。
ViewController.swift:18:22: Use of unresolved identifier 'DebugA'
ViewController.swift:19:22: Use of unresolved identifier 'DebugB'
屏蔽掉这两处代码运行,发现 has logo
和 has icon
都输出了。
所以 Debug/*
只能屏蔽 Debug
根目录下的文件,子文件夹无法屏蔽(因为 .xcasset
实质上也是一个文件夹,所以无法被屏蔽)。
如果确实想要屏蔽 Debug
下所有内容,要怎么处理呢?目前我还没找到比较好的方式😂,只能一个个目录排除了。拿上面的的结构来说,如果要排除文件夹 A
B
目录下的 Debug
,可能就得这样配置 Debug/*
Debug/*/*
Debug/*/*/*
,如图
![-w589](/media/2020-04-04/15859945623293.jpg)
所以这里支持的匹配模式主要有以下两种:
- 文件名完全匹配,比如
ModelA.Swift
- 通配符,如
Debug/*.Swift
Debug/*.*
*/Debug/*
Debug/*/*/*.png
Development Assets
![-w1219](/media/2020-04-04/15859936373369.jpg)
关于这个配置项可以查看 WWDC 2019 Session 233 Mastering Xcode Previews
Xcode 11
新增了一个 Development Assets
的配置项,简单尝试了下,但是似乎没有生效。我把上面目录结构中的 SubA.xcassets
加到里面,然后用release
模式跑起来还是能读到对应的图片,不知道是不是姿势不对。
其实重点还是 Excluded Source File Names
这个编译配置,有时候会有事半功倍的效果。
update 2022-09-22
最近使用 Development Assets 无意间发现,这个目录下的资源在 archive 出来的包会被排除(但 release 模式不会),具体情况如下
具体入口在选中 Target 之后,选中顶部 General 即可。