您现在的位置是:首页 >学无止境 >iOS无用类统计方案网站首页学无止境

iOS无用类统计方案

__future__ 2023-05-18 04:00:01
简介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;   //8bytes
    cache_t cache;      //16bytes
    class_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.html
        uint64_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这一套
  • 无法处理那些被初始化,但实际并未被使用的类
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。