查找Xcode工程未使用的图片资源

背景

随着APP的功能越来越丰富,安装包的尺寸也在快速增长。像16G这种在往日非常大的存储空间,现在也越来越捉襟见肘,安装包的大小已经成为衡量APP质量的指标之一。分析iOS的安装包文件.ipa文件我们可以看到,图片资源在安装包中还是占了比较大的比重,通过减少图片的总尺寸可以达到我们的目的。方法有两个,一是压缩单张图片的尺寸,比较著名的有imageOptim,二是减少图片的数量。实际上在我们的开发过程中,由于业务和设计的快速变化,导致工程中有许多冗余的图片资源。我们的目标就是找出并删除这些冗余的图片。为此,开发了工具UnusedImagesFinder

使用方法

使用方法自然是相当简单的,打开你的Terminal,执行以下命令:

1
python unused_images_finder.py PROJECT_PATH {SOURCE_PATH}

其中PROJECT_PATH是你的工程文件的路径,也就是.xcodeproj文件的路径。SOURCE_PATH是一个可选参数,一般设置为工程的根目录,默认为.xcodeproj文件的上级目录。检查的结果写入到当前目录下的result.txt文件中。按照上述的步骤执行,如果在result.txt文件中发现一堆误杀的文件,这时你就需要注意了,可能你的代码不是以常规的imageNamed:@"xxx"的形式引用的。此时,我们需要修改我们的源文件了,打开ununsed_images_finder找到174行,看到如下的代码:

1
imageReferencePatterns = [re.compile(r".*imageNamed:@\"([^@\.]+)(@\dx)*(\.png|\.jpg)*\".*")]

根据工程中引用图片的方式可以定制正则表达式,加入数组或者替换掉即可。

实现思路

大致的实现思路如下:

  • 找出图片资源

    在使用脚本中有一个参数是必填的,这个参数就是.xcodeproj,为什么一定要这个参数呢?因为在它的目录里有一个重要的文件project.pbxproj,这个文件包含了工程所有的构建信息,包括图片资源和编译源文件。打开project.pbxproj,找到Begin PBXResourcesBuildPhase sectionEnd PBXResourcesBuildPhase section之间的内容,这里就是工程所引用的资源,这里有一些plist,还有我们想要找的图片文件。通过正则匹配,我们可以拿到图片的名称。最初的版本,我就是这么写的,运行结果出来后,发现有非常多的漏查。查找后发现,工程中部分图片被放到.bundle文件中,其中的文件并不会出现在project.pbxproj,类似的还有image.xcasset中的图片资源。所以在这里改变策略,遍历整个工程,找出所有图片。

  • 找出源文件

    有了图片的查找经验,找资源文件就很简单了,所有被编译的资源都在project.pbxproj里的Begin PBXBuildFile sectionEnd PBXBuildFile section之间。为什么我们不是在遍历文件时找出所有源文件呢?因为我们有许多不好的习惯,很多时候没用的源文件只是简单地被从工程引用中移除,并没有被物理删除。如果我们把这部分文件也加进来,它们引用的图片将成为漏网之鱼。

  • 扫描未引用的图片资源

    有了上面两步的工作,第三步就比较简单了。遍历项目中的源文件,通过正则匹配的方式找到已引用的图片(注意,正则可能需要使用者补充,见使用方法),剩下的自然就是未被引用的资源了。

已知问题和改进

当前存在的不足如下:

  • 目前不支持Storyboardxib
  • 引用如果没有直接使用文件名,无法检测出,比如

    NSString *imageName = @"imageName";
    UIImage *image = [UIImage imageNamed:imageName];
    ...
    UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"xxx%d", 2]]
    
  • 误杀

    • Icon, Default 等文件不会直接被代码引用,查找的时候会被误杀
    • 问题二的文件同样会被误杀

未来改进的方向:

  • 改进上面的问题
  • 脚本集成到持续集成中
  • 提高检测率

最最最重要的一点,删除图片之前做好确认工作!