iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >iOS代码瘦身实践之如何删除无用的类
  • 957
分享到

iOS代码瘦身实践之如何删除无用的类

ios瘦身无用 2022-05-26 18:05:41 957人浏览 八月长安
摘要

前言 本文将提供一种静态分析的方式,用于查找可执行文件Mach-o中未使用的类,源码链接:xuezhulian/classunref (本地下载)。 Mach-o文件中_

前言

本文将提供一种静态分析的方式,用于查找可执行文件Mach-o中未使用的类,源码链接:xuezhulian/classunref (本地下载)。

Mach-o文件中__DATA __objc_classrefs段记录了引用类的地址,__DATA __objc_classlist段记录了所有类的地址,取差集可以得到未使用的类的地址,然后进行符号化,就可以得到未被引用的类信息。

引用类地址

可以通过Mac自带的工具otool打印Mach-o中的段信息,需要注意的是模拟器和真机对应的可执行文件,数据的存储方式不同需要加以区分。

可以通过file命令获取到arch。


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

在取类地址的时候区分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

通过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

所有类地址

通过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

取差集

用所有类信息减去引用类的信息,此时我们可以拿到未使用类的地址信息。


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

符号化

通过nm -nm命令可以得到地址和对应的类名字。


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

过滤

在实际分析的过程中发现,如果一个类的子类被实例化,父类未被实例化,此时父类不会出现在__objc_classrefs这个段里,在未使用的类中需要将这一部分父类过滤出去。使用otool -oV可以获取到类的继承关系。


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

为了防止一些三方库的误伤,还可以去过滤一些前缀,或者是是仅保留带有某些前缀的类。


 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)

最终结果保存在脚本目录下。


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()

这个思路在一定程度上能够减少代码的冗余,减小包的体积。因为是静态分析,不能包括动态调用的情况,对于需要删除的类需要进一步的确认。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对编程网的支持。

--结束END--

本文标题: iOS代码瘦身实践之如何删除无用的类

本文链接: https://www.lsjlt.com/news/30371.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 如何实现vbs后台运行bat删除自身的代码
    这篇文章主要为大家展示了“如何实现vbs后台运行bat删除自身的代码”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何实现vbs后台运行bat删除自身的代码”这篇文章吧。知道真实路径的情况下se...
    99+
    2023-06-08
  • 如何使用python删除前面的代码
    这期内容当中小编将会给大家带来有关如何使用python删除前面的代码,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。小型文件修改操作有一些通用的流程,删除文件的前n行,实际上也是对文件的修改,其中一个常用的...
    99+
    2023-06-15
  • PHP如何实现自动删除无用图片的程序
    这篇文章主要介绍“PHP如何实现自动删除无用图片的程序”,在日常操作中,相信很多人在PHP如何实现自动删除无用图片的程序问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”PHP如何实现自动删除无用图片的程序”的疑...
    99+
    2023-07-05
  • 如何轻轻松松删除此类无用的Found.000垃圾文件
    下面,笔者就给大家介绍一下如何删除此类无用的垃圾文件。     先按下&lquo;目录,将该目录下的“MDM.EXE”文件修改为“MDM.BAK”;再在“运行&...
    99+
    2023-05-25
    垃圾文件 Found.000
  • VUE 类型推断的艺术:如何在实践中提升代码质量
    类型推断的基础知识 类型推断是 VUE 语言中的一项特性,它允许编译器根据变量或表达式的值来推断出其类型,而无需程序员显式地指定类型。这使得 VUE 代码更加简洁、易读且健壮。 例如,在下面的代码中,编译器可以根据变量 name 的值来推...
    99+
    2024-02-15
    类型推断 VUE 代码质量 健壮性 可读性
  • ASP中的类:揭秘如何构建可重用代码之谜
    在ASP中,类是一种强大的工具,可用于构建可重用代码,实现软件模块化,并提高开发效率。通过使用类,您可以将数据和行为组织成逻辑单元,并通过对象实例化类来创建具有相同数据和行为的新实体。 一、创建类 在ASP中创建类非常简单,您只需要使用...
    99+
    2024-02-26
    ASP、类、面向对象编程、代码重用、继承、演示代码
  • 如何实现干掉进程并删除源文件的批处理bat代码
    小编给大家分享一下如何实现干掉进程并删除源文件的批处理bat代码,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!双击运行后,输入进程名称批处理代码:代码如下:@&n...
    99+
    2023-06-08
  • 如何使用JS代码自动删除稿件的普通弹幕功能
    这篇文章主要介绍如何使用JS代码自动删除稿件的普通弹幕功能,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1.删除弹幕的流程分析判断弹幕A是否为普通弹幕,是则选中。点击删除弹幕的按钮,...
    99+
    2024-04-02
  • PHP 类与对象最佳实践:学习如何编写出色的面向对象代码
    1. 定义类时要明确目的 在定义类之前,您应该首先考虑该类的目的。您希望它做什么?它应该具有什么样的属性和方法?一旦您明确了类的目的,就可以开始编写代码了。 2. 使用命名空间来组织类 命名空间可以帮助您将类组织成不同的类别,使代码更容...
    99+
    2024-02-25
    PHP 面向对象编程 对象 最佳实践
  • 如何使用jQuery实现一个类似GridView编辑,更新,取消和删除的功能
    这篇文章主要为大家展示了“如何使用jQuery实现一个类似GridView编辑,更新,取消和删除的功能”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何使用jQ...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作