> 文章列表 > iOS无用类统计方案

iOS无用类统计方案

iOS无用类统计方案

网上能搜到各种无用类统计,多数是通过对编译产物进行分析。类似这种方案有个问题:如果类被引用但实际上用户已不会访问到,这种是束手无策的。

新方案

前年在公司做了个新的方案,主要技术点是利用OC类初始化标记initialized,学习过objc源码的同学应该熟悉,类初始化之后有个bit位被标记为1,因此在特定的时机遍历工程所有类,并检查这个比特位是否为1即可筛选出所有被使用过或未被用过的类。

代码如下:

#define FAST_DATA_MASK  0x00007ffffffffff8UL
#define RW_INITIALIZED  (1<<29)/*
// objc_class 代码片段
struct objc_class : objc_object {//8bytes// Class ISA;Class superclass;   //8bytescache_t cache;      //16bytesclass_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
...class_rw_t *data() const {return bits.data();}
...bool isInitialized() {return getMeta()->data()->flags & RW_INITIALIZED;}
...
}
*/
bool class_hasInitialized(const char * _Nullable className) {Class metaCls = objc_getMetaClass(className);if (metaCls) {// https://opensource.apple.com/source/objc4/objc4-787.1/runtime/objc-runtime-new.h.auto.htmluint64_t *bits = (__bridge void *)metaCls + 32;uint32_t *data = (uint32_t *)(*bits & FAST_DATA_MASK);uint64_t result = (*data & RW_INITIALIZED);return result != 0;}return false;
}

核心代码很简单,获取工程所有类可以参考之前的文章

大概的统计思路:

前端

前端负责上传使用过的类
  1. 检测到用户升级新版本后,把所有类写入KV
  2. 定时/特定时机遍历KV中的所有类,检查是否被初始化,将被初始化的从KV中移除并上传后台(即端上KV中保留的总是未使用的,上传的总是新增的使用过的)

后端

后端通过集合去差集的方式统计所有未使用过的类
  1. 在接收到用户上传时,如果后端没有所有类的集合,则给客户端返回一个特定code,端上据此上传全量类的集合
  2. 端上根据全量类的集合以及用户上传的使用过的集合做差集,即为未使用过的集合。

逻辑增强

统计是针对版本进行的,因此为了数据更精确可以针对每个版本的统计数据做最近n个版本的交集,表示最近n个版本都未使用过。如此以来删无用代码的确认压力可以减小很多。

问题

  • 无法统计纯Swift类,因为Swift是静态类不走runtime这一套
  • 无法处理那些被初始化,但实际并未被使用的类