介绍
这篇文章将为大家详细讲解有关iOS代码瘦身实践之怎么删除无用的类,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
<强>前言强>
本文将提供一种静态分析的方式,用于查找可执行文件Mach-o中未使用的类,源码链接:xuezhulian/classunref (本地下载)。
Mach-o文件中__DATA __objc_classrefs段记录了引用类的地址,__DATA __objc_classlist段记录了所有类的地址,取差集可以得到未使用的类的地址,然后进行符号化,就可以得到未被引用的类信息。
<强>引用类地址强>
可以通过Mac自带的工具otool打印Mach-o中的段信息,需要注意的是模拟器和真机对应的可执行文件,数据的存储方式不同需要加以区分。
可以通过文件命令获取到拱门。
# binary_file_arch: distinguish Big-Endian 以及低位优先 # file -b output 例如:,Mach-O 64 - bit executable arm64 binary_file_arch =, os.popen (& # 39; file -b & # 39;, +,路径).read () .split (& # 39;, & # 39;) [1] .strip ()
在取类地址的时候区分x86_64和手臂。
def pointers_from_binary(线,,binary_file_arch):=,行,line  [16:] .strip () .split (& # 39;, & # 39;)=,pointers 设置()==,if binary_file_arch & # 39; x86_64 # 39;: #才能untreated line 例如:00000001030 cec80 d8 75年,15,03年,01,00,00,00,68,77,15,03年,01,00,00 00 pointers.add才能(& # 39;& # 39;. join(线[4:8][::1],+,行[0:4][::1])) pointers.add才能(& # 39;& # 39;. join(线[12:16][::1],+,行[8:12][::1])) return 才能指针 ,# arm64 证实,armv7 arm7s 未经证实 ,if binary_file_arch.startswith(& # 39;手臂# 39;): #才能untreated line 例如:00000001030 bcd20 03138580 00000001, 03138878, 00000001 pointers.add才能([1]行,+,行[0]) pointers.add才能(行[3],+,行[2]) return 才能指针 ,return 没有
通过otool - v - s __DATA __objc_classrefs获取到引用类的地址。
def class_ref_pointers(路径,binary_file_arch): ref_pointers =,才能设置() lines 才能=,os.popen (& # 39;/usr/bin/otool -v -s __DATA __objc_classrefs % & # 39;, %,路径). readlines () for 才能;line 拷贝: ,,,pointers =, pointers_from_binary(线,,binary_file_arch) ,,,ref_pointers =, ref_pointers.union(指针) return 才能ref_pointers
<强>所有类地址强>
通过otool - v - s __DATA __objc_classlist获取所有类的地址。
def class_list_pointers(路径,binary_file_arch): list_pointers =,才能设置() lines 才能=,os.popen (& # 39;/usr/bin/otool -v -s __DATA __objc_classlist % & # 39;, %,路径). readlines () for 才能;line 拷贝: ,,,pointers =, pointers_from_binary(线,,binary_file_arch) ,,,list_pointers =, list_pointers.union(指针) return 才能list_pointers
<>强取差集强>
用所有类信息减去引用类的信息,此时我们可以拿到未使用类的地址信息。
unref_pointers =, class_list_pointers(路径,binary_file_arch),安康;class_ref_pointers(路径,,binary_file_arch)
<强>符号化
强>
通过纳米纳米命令可以得到地址和对应的类名字。
def class_symbols(路径): symbols 才能=,{} #才能class symbol  format 得到纳米:,0000000103113 f68 (__DATA __objc_data), external _OBJC_CLASS_ _EpisodeStatusDetailItemView美元 re_class_name 才能=,re.compile (& # 39; (\ w {16}),。*, _OBJC_CLASS_ \ $ _ (+) & # 39;) lines 才能=,os.popen (& # 39; nm  -nm % & # 39;, %,路径). readlines () for 才能;line 拷贝: ,,,result =, re_class_name.findall(线) ,,,if 结果: ,,,,,(地址,符号),[0]=,结果 ,,,,,符号(地址),=,象征 return 才能;符号
<>强过滤强>
在实际分析的过程中发现,如果一个类的子类被实例化,父类未被实例化,此时父类不会出现在__objc_classrefs这个段里,在未使用的类中需要将这一部分父类过滤出去。使用otool ov可以获取到类的继承关系。
def filter_super_class (unref_symbols): re_subclass_name 才能=,re.compile (“\ w {16}, 0 x \ w {9}, _OBJC_CLASS_ \ $ _ (+)“) re_superclass_name 才能=,re.compile (“\ s * superclass  0 x \ w {9}, _OBJC_CLASS_ \ $ _ (+)“) # subclass 例如:才能,0000000102 bd8070 0 x103113f68 _OBJC_CLASS_ _TTEpisodeStatusDetailItemView美元 # superclass 例如:才能,superclass 0 x10313bb80 _OBJC_CLASS_ _TTBaseControl美元 时间=lines 才能;os.popen (“/usr/bin/otool  -oV % s", %,路径). readlines () null null null null null null null null null null null null null null null null null null null null null null null null null null null null nulliOS代码瘦身实践之怎么删除无用的类