清理无用代码及资源

益处

  • 提高代码可维护性
  • 减少编译时间
  • 减小包体积大小
  • 获得一个好心情

清理无用类型(class struct enum protocol)、未使用到的方法

使用 pecker 检测,peckerRoyCao 开发的一款 自动检测项目中不用的代码 的工具。在 github 已有 1.1k 的 star。

其原理大致如下:

  1. 找到项目中所有的类和方法等(SwiftSyntax)
  2. 在DerivedData找到项目的Index,初始化IndexStoreDB
  3. 通过IndexStoreDB查找符号,查看关系,是否有引用,确定是否被使用
  4. 显示Warning

详细请看作者的文章github介绍

下面是我使用的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
reporter: "xcode"

disabled_rules:
# - skip_public

included: # paths to include during detecting. `--path` is ignored if present.
- ./

excluded: # paths to ignore during detecting. Takes precedence over `included`.
- Carthage
- Pods
- xx/ThirdPart
- xx/Util
- xx/Extension

excludedGroupName: # names of group to ignore during detecting.
- SwiftPeckerTestUITests

blacklist_files: # files to ignore during detecting, only need to add file name, the file extension default is swift.
- HomeViewController

blacklist_symbols: # symbols to ignore during detecting, contains class, struct, enum, etc.
- AppDelegate
- viewDidLoad

# blacklist_superclass: # all the class inherit from class specified in the list will ignore
# - UITableViewCell

清理效果为:

减少代码 310945 - 301399 = 9546
减少文件 2164 - 2066 = 58

进一步清理无用代码

上面的工具只能检测参与编译 swift 源文件的使用情况,对于散落在项目目录下的未添加到工程的文件就不起作用了。

虽然这些文件在 xcode 中不会出现,但在 vscode 等其他编辑器中依然会出现,检索代码时会检索出来,造成理解障碍。另外,这些文件会随着项目的发展越来越多,拖慢 git 的速度,最后,代码洁癖患者真的无法忍受这种情况。

这里提供一个检测思路,将项目目录下所有源文件的集合所有参与编译的源文件的集合的差集,既是我们要清理的文件集合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# ①----------------------------------------------------------------------------------------------------------------------------------------------------------------------------#
require 'xcodeproj'
# 打开 project,将 name.project 解析为 Xcodeproj 的 Project 对象
project_path = "/Users/abc/workspace/projectName/projectName.xcodeproj"
project = Xcodeproj::Project.open(project_path)

# 拿到 build phases 中 Compile Sources 下的文件列表
# 这里只简单取了第一个 target,对于多 target 的项目,请修改后使用
build_phases = project.targets[0].build_phases
# 由于 Compile Source 这个 phase 的位置不固定,所以需要找到其位置(index)
# Compile Source 的 display_name 不是 Compile Source 而是 Sources, 其类型是 Xcodeproj::Project::Object::PBXSourcesBuildPhase
# 注意 xcode UI 界面的 build phases 中的第一个 `Dependencies` 不属于一个 `phase`, 所以第一个index不是 Dependencies
index_of_compile_source_phase = build_phases.index { |b|
b.display_name == "Sources" && b.class == Xcodeproj::Project::Object::PBXSourcesBuildPhase
}
# 通过 files_references 拿到所有文件,记录其 display_name,即文件名
build_files = build_phases[index_of_compile_source_phase].files_references.map { |f| f.display_name if !f.nil?}
# 将其转为集合,后续使用,这里不会有同名文件
build_files = Set.new(build_files)
puts "Build Source phase 下的编译文件数:#{build_files.count}"

# ②----------------------------------------------------------------------------------------------------------------------------------------------------------------------------#
# 拿到项目目录下所有的 .swift .m .mm 等参与编译的文件,抛弃路径信息,只保留文件名
# 这里可能会有同名文件
folder_files = `find /Users/abc/workspace/projectName/projectName -name '*.swift' -o -name '*.m' -o -name '*.mm'`.split("\n").map { |f| f.split("/").last }
puts "项目目录下的编译文件数:#{folder_files.count}"

# 找出所有同名文件,并放到数组 d 中
# 同名文件也是需要处理的一部分,需要手动查看文件内容,确认是否需要保留
dict = {}
d = []
folder_files.each { |f|
if dict[f] == nil then
dict[f] = 1
else
count = dict[f]
count += 1
dict[f] = count
puts "同名文件:#{f}"
d.append(f)
end
}
# 将其转为集合,后续使用
folder_files = Set.new(folder_files)
puts "去重后项目目录下的编译文件数:#{folder_files.count}"

# ③----------------------------------------------------------------------------------------------------------------------------------------------------------------------------#
duplicate = folder_files - build_files
puts "冗余文件数量为:#{duplicate.count}"
puts Array(duplicate).each { |f| f }

所有参与编译的源文件

参与项目编译的源文件都在 build phases 中的 Compile Source 这个 phase 下,如果能拿到这个列表,就得到了所有参与编译的源文件

可以通过使用 xcodeproj 这个库来拿到, 代码见上方 ①

项目目录下所有源文件

可以遍历项目目录,拿到所有源文件,可以用 ruby 实现, 但比较麻烦,这里使用 shellfind 命令来查找。
代码见上方 ②

找出无用文件

上面两步找到了所需要的两个集合,build_filesfolder_files, folder_files 会是 build_files 的超集, 除非你参与编译的源文件不在项目目录下,这样的话你就需要考虑这么做是不是合理了。这里不考虑这种情况。

要找出无用文件只需要将两个集合取差集即可,代码见上方 ③

清理结果

减少代码 301399 - 294501 = 6898
减少文件 2066 - 2046 = 20

清理无用资源文件

无用代码清理完毕后,才是清理无用资源文件的最好时机。
检测无用资源文件,推荐使用 onevcatFengNiaotinymindLSUnusedResources

与我而言,更喜欢在 vscode 的终端下使用 FengNiao 来检测,可以点击链接直接预览图片和复制名称。使用 LSUnusedResources 来核验检测效果。

清理效果
资源数减少 1141 - 536 = 605
文件大小减少 34.68M
ipa 的下载大小和安装大小尚未测试