国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 系統(tǒng) > iOS > 正文

iOS代碼瘦身實(shí)踐之如何刪除無(wú)用的類

2020-07-26 02:19:18
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

前言

本文將提供一種靜態(tài)分析的方式,用于查找可執(zhí)行文件Mach-o中未使用的類,源碼鏈接:xuezhulian/classunref (本地下載)。

Mach-o文件中__DATA __objc_classrefs段記錄了引用類的地址,__DATA __objc_classlist段記錄了所有類的地址,取差集可以得到未使用的類的地址,然后進(jìn)行符號(hào)化,就可以得到未被引用的類信息。

引用類地址

可以通過(guò)Mac自帶的工具otool打印Mach-o中的段信息,需要注意的是模擬器和真機(jī)對(duì)應(yīng)的可執(zhí)行文件,數(shù)據(jù)的存儲(chǔ)方式不同需要加以區(qū)分。

可以通過(guò)file命令獲取到arch。

#binary_file_arch: distinguish Big-Endian and Little-Endian#file -b output example: Mach-O 64-bit executable arm64binary_file_arch = os.popen('file -b ' + path).read().split(' ')[-1].strip()

在取類地址的時(shí)候區(qū)分x86_64和arm。

def pointers_from_binary(line, binary_file_arch): line = line[16:].strip().split(' ') pointers = set() if binary_file_arch == 'x86_64':  #untreated line example:00000001030cec80	d8 75 15 03 01 00 00 00 68 77 15 03 01 00 00 00  pointers.add(''.join(line[4:8][::-1] + line[0:4][::-1]))  pointers.add(''.join(line[12:16][::-1] + line[8:12][::-1]))  return pointers #arm64 confirmed,armv7 arm7s unconfirmed if binary_file_arch.startswith('arm'):  #untreated line example:00000001030bcd20	03138580 00000001 03138878 00000001  pointers.add(line[1] + line[0])  pointers.add(line[3] + line[2])  return pointers return None

通過(guò)otool -v -s __DATA __objc_classrefs獲取到引用類的地址。

def class_ref_pointers(path, binary_file_arch):  ref_pointers = set()  lines = os.popen('/usr/bin/otool -v -s __DATA __objc_classrefs %s' % path).readlines()  for line in lines:    pointers = pointers_from_binary(line, binary_file_arch)    ref_pointers = ref_pointers.union(pointers)  return ref_pointers

所有類地址

通過(guò)otool -v -s __DATA __objc_classlist獲取所有類的地址。

def class_list_pointers(path, binary_file_arch):  list_pointers = set()  lines = os.popen('/usr/bin/otool -v -s __DATA __objc_classlist %s' % path).readlines()  for line in lines:    pointers = pointers_from_binary(line, binary_file_arch)    list_pointers = list_pointers.union(pointers)  return list_pointers

取差集

用所有類信息減去引用類的信息,此時(shí)我們可以拿到未使用類的地址信息。

unref_pointers = class_list_pointers(path, binary_file_arch) - class_ref_pointers(path, binary_file_arch)

符號(hào)化

通過(guò)nm -nm命令可以得到地址和對(duì)應(yīng)的類名字。

def class_symbols(path):  symbols = {}  #class symbol format from nm: 0000000103113f68 (__DATA,__objc_data) external _OBJC_CLASS_$_EpisodeStatusDetailItemView  re_class_name = re.compile('(/w{16}) .* _OBJC_CLASS_/$_(.+)')  lines = os.popen('nm -nm %s' % path).readlines()  for line in lines:    result = re_class_name.findall(line)    if result:      (address, symbol) = result[0]      symbols[address] = symbol  return symbols

過(guò)濾

在實(shí)際分析的過(guò)程中發(fā)現(xiàn),如果一個(gè)類的子類被實(shí)例化,父類未被實(shí)例化,此時(shí)父類不會(huì)出現(xiàn)在__objc_classrefs這個(gè)段里,在未使用的類中需要將這一部分父類過(guò)濾出去。使用otool -oV可以獲取到類的繼承關(guān)系。

def filter_super_class(unref_symbols):  re_subclass_name = re.compile("/w{16} 0x/w{9} _OBJC_CLASS_/$_(.+)")  re_superclass_name = re.compile("/s*superclass 0x/w{9} _OBJC_CLASS_/$_(.+)")  #subclass example: 0000000102bd8070 0x103113f68 _OBJC_CLASS_$_TTEpisodeStatusDetailItemView  #superclass example: superclass 0x10313bb80 _OBJC_CLASS_$_TTBaseControl  lines = os.popen("/usr/bin/otool -oV %s" % path).readlines()  subclass_name = ""  superclass_name = ""  for line in lines:    subclass_match_result = re_subclass_name.findall(line)    if subclass_match_result:      subclass_name = subclass_match_result[0]    superclass_match_result = re_superclass_name.findall(line)    if superclass_match_result:      superclass_name = superclass_match_result[0]    if len(subclass_name) > 0 and len(superclass_name) > 0:      if superclass_name in unref_symbols and subclass_name not in unref_symbols:        unref_symbols.remove(superclass_name)      superclass_name = ""      subclass_name = ""  return unref_symbols

為了防止一些三方庫(kù)的誤傷,還可以去過(guò)濾一些前綴,或者是是僅保留帶有某些前綴的類。

 for unref_pointer in unref_pointers:    if unref_pointer in symbols:      unref_symbol = symbols[unref_pointer]      if len(reserved_prefix) > 0 and not unref_symbol.startswith(reserved_prefix):        continue      if len(filter_prefix) > 0 and unref_symbol.startswith(filter_prefix):        continue      unref_symbols.add(unref_symbol)

最終結(jié)果保存在腳本目錄下。

script_path = sys.path[0].strip()f = open(script_path+"/result.txt","w")f.write( "unref class number:  %d/n" % len(unref_symbles))f.write("/n")for unref_symble in unref_symbles:  f.write(unref_symble+"/n")f.close()

這個(gè)思路在一定程度上能夠減少代碼的冗余,減小包的體積。因?yàn)槭庆o態(tài)分析,不能包括動(dòng)態(tài)調(diào)用的情況,對(duì)于需要?jiǎng)h除的類需要進(jìn)一步的確認(rèn)。

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)武林網(wǎng)的支持。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 海宁市| 西宁市| 寻乌县| 邹平县| 昌江| 永嘉县| 内乡县| 连南| 科技| 鄄城县| 浦东新区| 尉犁县| 基隆市| 库尔勒市| 赤城县| 泸水县| 布拖县| 佛山市| 铁力市| 陕西省| 奉新县| 肃北| 三亚市| 朝阳区| 湘潭市| 同德县| 额济纳旗| 漠河县| 山东| 普兰县| 普兰店市| 金华市| 顺平县| 哈巴河县| 卢氏县| 岱山县| 抚州市| 绍兴市| 永安市| 拉萨市| 通渭县|