背景:
日常的项目经过长时间的迭代,优化,重构之后,一些方法可能已经用不到了,但是并未及时删除,长久下去,会影响我们的包大小。
如果你近期有进行代码瘦身的打算,可以看一下。
脚本使用方式
python FindSelectorsUnrefs.py -a /Users/a58/Library/Developer/Xcode/DerivedData/XXX-bqqoxganvkvgwuefbskxsbvnxlnn/Build/Products/Debug-iphonesimulator/XXX.app -p /Users/a58/Desktop/ProjectPath -w WB,JD
参数说明:
-a Xcode运行之后的,项目Product路径
-p 项目的地址
-w 结果白名单处理,检测结果,只想要以什么开头的类的方法,多个用逗号隔开,比如JD,BD,AL
-b 结果黑名单处理,检测结果,不想要以什么开头的类的方法,多个用逗号隔开,比如Pod,AF,SD
-w 和 -b 不能共存,共存会报错
-a的存储路径
运行结果
获取所有的protocol中的方法...
获取所有被调用的方法...
获取所有的方法,除了setter and getter方法...
查找到未被使用的方法: 170个
1 : +[WBNotificationProxy getNotifyListWithPageNum:success:failure:]
2 : -[AJKActionSheetBackgroundView touchesCancelled:withEvent:]
3 : +[WBPageRouteConfig getRequestRouteModeltWithClassName:params:]
。。。。。。。。
166 : -[WBLaunchAdManager batchDownloadVideoAndCache]
167 : -[AJKPopupView initWithPopSize:]
168 : -[WBWebViewViewController gobackAction:]
169 : -[WBFaceIDCardLiveStillTool startDetect:imageURLPathHost:imageURLShortPath:viewController:]
170 : -[WBRunLog uploadReportBackCatch]
项目中未使用方法检测完毕,相关结果存储到当前目录 selector_unrefs.txt 中
请在项目中进行二次确认后处理
流程结果图
详细工作流程:
实现分析
通过使用otool工具对编译产生的Mach-O文件,结合项目源码进行分析。
第一步:
找到项目中所有的protocol
中的方法<包括系统<UITabaleViewDelegate>
的和项目的>
第二步:
通过Mach-O,找到项目中所有被引用的方法
第三步:
通过Mach-O,找到项目中所有的方法
第四步:
遍历第三步查出的所有方法,如果当前方法不在代理方法集合,也不在引用方法集合中,那么就认为是没有用到的方法。
原理篇
Mach-O简介
关于Mach-O更详细的每一部分的介绍可以参考
https://www.cnblogs.com/dengzhuli/p/9952202.html
otool简介
otool可以提取并显示iOS下Mach-O的相关信息,包括头部,加载命令,各个段,共享库,动态库等等。它拥有大量的命令选项,是一个功能强大的分析工具,当然还可以做反汇编的工具使用。
比如:
# 获取项目中已经使用的方法
otool -v -s __DATA __objc_selrefs SelectorsUnrefs
接下来按照上面流程图的顺序进行回顾
1、找到项目中所有的protocol中的方法<包括系统<UITabaleViewDelegate>的和项目的>
1.1、获取当前项目引用的所有的Libraries
mach-o path 是当前mach-o文件路径
otool -L mach-o path
otool -L SelectorsUnrefs
SelectorsUnrefs:
/System/Library/Frameworks/ImageIO.framework/ImageIO (compatibility version 1.0.0, current version 0.0.0)
/System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 1676.104.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1)
/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation (compatibility version 150.0.0, current version 1676.104.0)
/System/Library/Frameworks/UIKit.framework/UIKit (compatibility version 1.0.0, current version 61000.0.0)
1.2、获取所有Libraries中的.h文件目录
find /System/Library/Frameworks/** -name \"*.h\"
1.3、获取项目中的所有.h文件目录
find 项目地址 -name \"*.h\"
project_path == /Users/a58/Desktop/smallDemo/SelectorsUnrefs
/Users/a58/Desktop/smallDemo/SelectorsUnrefs/SelectorsUnrefs/AppDelegate.h
/Users/a58/Desktop/smallDemo/SelectorsUnrefs/SelectorsUnrefs/Dog.h
/Users/a58/Desktop/smallDemo/SelectorsUnrefs/SelectorsUnrefs/ClickView.h
/Users/a58/Desktop/smallDemo/SelectorsUnrefs/SelectorsUnrefs/ShowView.h
/Users/a58/Desktop/smallDemo/SelectorsUnrefs/SelectorsUnrefs/ViewController.h
1.4、取合集
获取当前项目中所有的.h文件目录
将上面两部分.h路径合并为一个集合
1.5、通过正则找到.h中所有的@protocol下面的方法
1.6、生成集合protocol_sels
protocol_sels
集合中包含了所有的代理方法
2、获取项目所有的引用方法
2.1、获取项目所有被引用的方法
otool -v -s __DATA __objc_selrefs
SelectorsUnrefs:
Contents of (__DATA,__objc_selrefs) section
0000000100006000 __TEXT:__objc_methname:viewDidLoad
0000000100006008 __TEXT:__objc_methname:initWithFrame:
0000000100006010 __TEXT:__objc_methname:redColor
。。。。。。
0000000100006098 __TEXT:__objc_methname:addGestureRecognizer:
00000001000060a0 __TEXT:__objc_methname:clickBlock
00000001000060a8 __TEXT:__objc_methname:setClickBlock:
2.2、整理成ref_sels
viewDidLoad
initWithFrame:
redColor
setBackgroundColor:
setDelegate:
view
。。。。。
clickBlock
setClickBlock:
3、获取项目所有的方法
3.1、使用otool获取项目所有的方法
otool -oV mach-o path
3.2、一块干掉所有属性的get和set方法
这里不过滤setter和getter,因为作为属性,可能会被YYModel之类的进行解析
3.3、删除 load 、.cxx_destruct等方法
load
方法不用说,肯定会被调用的
.cxx_destruct
方法原本是为了C++
对象析构的,ARC
借用了这个方法插入代码实现了自动内存释放的工作
3.4、整理成imp_sels
imp_sels
包含了项目中所有的方法,除了load
,setter
,getter
方法
4、遍历imp_sels
如果方法不在protocol_sels
也不在ref_sels
中认为没有被用到
5、黑白名单过滤
黑名单过滤:
我们在查找完毕之后,可能会有很多的类的方法,有些类的方法我们没必要修改也修改不了,比如Pods中的类,比如MJRefresh
中检测出一个无用类方法,我们也不能删除,作为工具类多余的方法在以后不一定没用。
我们可以选择不看,比如,输入参数 -b MJ,AF
python FindSelectorsUnrefs.py -a /Users/a58/Library/SelectorsUnrefs.app -p /Users/a58/Desktop/smallDemo/SelectorsUnrefs -b MJ,AF
那么我们的结果中会把MJ
和AF
开头的干掉,方便查看
白名单:
我们只想看我们自己的类中的方法,一般项目的类都会有固定的开头,方便管理
比如,我们的类名都是以WB开头的,输入参数 -w WB
,那么结果只会显示WB
开头的未使用类的方法
6、检测结果写入日志文件
结果可能过多,终端显示不开,将结果写入文件,方便阅读
7、根据检测结果,在项目中二次确认后处理
检测结果是有偏差的,有些已使用的类方法仍然不太好检测。
已知的情况:
1、如果两个类中的方法名称一样,如果其中一个类的方法使用了,可能会影响另外一个类中的方法检测结果。因为在获取已用方法列表的时候,只获取到了一个方法,如果其中一个类的方法使用了,另一个可能会被误判使用。不过一般情况下,这种情况比较少。
检测不到的情况
1、方法1调用方法2,但是没有类调用方法1。
只能检测出方法1
将方法1删除之后才会检测出方法2,所以需要检测多次
2、Swift检测不到
3、C语言方法检测不到,会被认为是已使用方法
4、通过字符串转化成Selector检测不到,会被认为是为被调用的方法<后期可以考虑添加策略>