精华内容
下载资源
问答
  • 不断的开发迭代,产品经理不断的添加需求,引入的资源文件几乎是只加不减,猛然回首,iOS包已经100多m,看来iOS瘦身迫在眉睫啊!!!!iOS瘦身的好处我们先来讨论 iOS瘦身的好处,正所谓知其然知其所以然。iOS瘦身有...

    不断的开发迭代,产品经理不断的添加需求,引入的资源文件几乎是只加不减,猛然回首,iOS包已经100多m,看来iOS瘦身迫在眉睫啊!!!!

    iOS瘦身的好处


    我们先来讨论 iOS瘦身的好处,正所谓知其然知其所以然。iOS瘦身有哪些好处?

    1. However, consider download times when determining your app’s size. Minimize the file’s size as much as possible, keeping in mind that there is a 100 MB limit for over-the-air downloads. Abnormally large build files are usually the result of storing data, such as images, inside the compiled binary itself instead of as a resource inside your app bundle. If you are compiling an image or large dataset into your binary, it would be best to split this data out into a resource that is loaded dynamically by your app.

    1.首先对于新用户当第一安装你的iOS程序时,需要下载完整的一个.ipa文件。(注意不同于升级),相同环境下iOS包越小用户的下载时间越少,这一点对于用户体验来说至关重要,特别是有些情况apple store访问不太顺利,用户下载你的iOS包肯定是迫切想了解使用你的产品,如果用户等半天(等待对于用户总是漫长的),用户会烦躁带着这种心情去了解你的产品,第一印象肯定不爽

    2.回想起来你的app第一个版本,有可能才10几m,才一年多的时间,你的app已经变成100m了,然而产品业务需求是不断迭代的,如果不加以控制,后面只会越来越大

    3.可能绝大部分的原因,你的产品并非是强势app(比如支付宝,qq,百度)所以你也就不能随便耍流氓,用户手机的存储空间是有限的(特别是那些16g的iPhone),除了那些强势app,你必须要在剩余不多的空间,争取用户不会删除你的app(特别是那些工具类型app)

    4.less is more ,对于代码和那些资源文件也是如此,无用的代码和资源文件越多,就导致项目冗余越大,维护也越麻烦,比如很可能同一张图片有好几个不同命名,如果要改图片了就要全部更换 !

    知道了瘦身的好处接下来,我们就谈论一下iOS该怎么瘦身

    iOS该怎么瘦身  


    在做任何相关优化之前,我们需要做一些权衡。通过权衡,可以知道把优化的重点集中在什么地方。我们上文提到,当第一安装iOS程序时,需要下载完整的一个.ipa文件。实际上.ipa文件就是一个.zip结构。简单的将后缀为.ipa文件修改为.zip,然后利用Finder将其解压出来。右键单击解压出来的.app bundle,选择显示包内容,以查看里面的资源文件。通过该方法我们可以看到哪些文件占的空间最大。记住:.app bundle是经过压缩的,并且有些文件的压缩效果要比别的文件好,所以压缩后的效果才是才是最重要的。不过一般情况下在压缩前最大的文件,在压缩后依旧是最大的文件。

    而这些资源文件(包括图片、声音以及其它配置文件)通常占了ipa 很大部分,所以我首先针对资源文件优化。

    (1).资源文件瘦身

    a.删除无用的图片资源

    一个项目开发的越久,添加的功能模块就越多,相应的,也会慢慢引入更多的图片资源。但是在移除一些不再使用的模块时,开发者往往不会将对应的图片资源一起删除,因为图片资源和源码是分离的,长久以来,项目中就会出现大量没有使用的图片资源。删除无用的资源一般来说 能减掉个2~3MB。 这个时候就要使用工具自动迅速找出工程中所有没被使用的资源文件喽(想想就知道了 ,用手工得多慢多累啊) ,工欲善其事,必先利其器。

    我首先推荐的是https://github.com/tinymind/LSUnusedResources  整个过程非常的快, 比shell脚本不知道方便到哪里去了, 为了照顾那些懒癌症患者 我把使用方法也贴出来

    使用方法如下

    1.点击 Browse.. 选择一个文件夹;
    2.点击 Search 开始搜索;



    3.等待片刻即可看到结果;

    4.选中某些行,然后点击 Delete 可以直接删除资源

    第二种方法就是你也可以用万能的脚本 https://github.com/examplecode/unused-image/blob/master/unused-image.sh   &&  http://stackoverflow.com/questions/6113243/how-to-find-unused-images-in-an-xcode-project/6113449#6113449

    b.对资源压缩

    首先 尽量使用8-bit的PNG图片,比32-bit的图片能减少4倍的压缩率。由于8-bit的图片支持最多256种不同的颜色,所以8-bit的图片一般只应该用于一小部分的颜色图片。例如灰度图片最好使用8-bit。然后并不能事事都如意, 设计师提供的图片资源往往都是直接从sketch中剪切后的资源,大小非常大,这个时候就需要对png进行无损压缩,假设我们的项目中有30M的图片,然后将它们有损压缩到80%的质量,那么就可以减掉6MB左右。可以使用以下两种方法进行图片的压缩:用的是ImageOptim工具和compress命令(具体怎么使用我不想写了)。但是并不建议对资源做有损压缩,因为有损压缩通常压缩后效果不尽人意需要设计一个个检查。

    c. BitCode

    首先我们来介绍一下  BitCode 是啥?

    Bitcode is an intermediate representation of a compiled program. Apps

    you upload to iTunes Connect that contain bitcode will be compiled and

    linked on the App Store. Including bitcode will allow Apple to

    re-optimize your app binary in the future without the need to submit a

    new version of your app to the store.

    说的是bitcode是被编译程序的一种中间形式的代码。包含bitcode配置的程序将会在App store上被编译和链接。bitcode允许苹果在后期重新优化我们程序的二进制文件,而不需要我们重新提交一个新的版本到App store上。呀,真有这么高级?

    LLVM是目前苹果采用的编译器工具链,Bitcode是LLVM编译器的中间代码的一种编码,LLVM的前端可以理解为C/C++/OC/Swift等编程语言,LLVM的后端可以理解为各个芯片平台上的汇编指令或者可执行机器指令数据,那么,BitCode就是位于这两者直接的中间码. LLVM的编译工作原理是前端负责把项目程序源代码翻译成Bitcode中间码,然后再根据不同目标机器芯片平台转换为相应的汇编指令以及翻译为机器码.这样设计就可以让LLVM成为了一个编译器架构,可以轻而易举的在LLVM架构之上发明新的语言(前端),以及在LLVM架构下面支持新的CPU(后端)指令输出,虽然Bitcode仅仅只是一个中间码不能在任何平台上运行,但是它可以转化为任何被支持的CPU架构,包括现在还没被发明的CPU架构,也就是说现在打开Bitcode功能提交一个App到应用商店,以后如果苹果新出了一款手机并CPU也是全新设计的,在苹果后台服务器一样可以从这个App的Bitcode开始编译转化为新CPU上的可执行程序,可供新手机用户下载运行这个App.

    扯了这么多 ,推出Bitcode的好处是啥? 跟iOS瘦身啥关系?之前打包,可以运行在各个不同型号的iOS设备上,是因为在打包的时候,苹果帮我们把app在各型号设备上运行所需要的“东西”一并全部打到包里了。假设我们在打包的时候,只把要运行的设备所需的“东西”打到包里,而不需要其他型号运行所需要的“东西”,这样不就达到减小ipa大小的目的了么?BitCode就是来完成这个任务的中间件。

    d.正确导入图片的姿势


    图片的导入方式有如下几种:

    1.加入到Assets.xcassets中

    只支持png格式的图片

    图片只支持[UIImage imageNamed]的方式实例化,但是不能从Bundle中加载

    在编译时,Images.xcassets中的所有文件会被打包为Assets.car的文件

    2.CreateGroup

    黄色文件夹图标;Xcode中分文件夹,Bundle中所有所在都在同一个文件夹下,因此,不能出现文件重名的情况

    可以直接使用[NSBundle mainBundle]作为资源路径,效率高!

    可以使用[UIImage imageNamed:]加载图像

    3.CreateFolderRefences

    蓝色文件夹;Xcode中分文件夹,Bundle中同样分文件夹,因此,可以出现文件重名的情况

    需要在[NSBundle mainBundle]的基础上拼接实际的路径,效率较差

    不能使用[UIImage imageNamed:]加载图

    4.PDFs矢量图(Xcode6+)

    5.Bundle(包)中的图片素材

    那这不同的导入方式,会对打出的包的大小有影响么?

    经过测试得知:CreateGroup、CreateFolderRefences两种方式打出来的包,图片都会直接放在.app文件中,所以打包前后,图片的大小不会改变

    而加入到Assets.xcassets中的方法则不同,打包后,在.app中会生成Assets.car文件来存储Assets.xcassets中的图片,并且文件大小方面也大大降低

    所以,使用Assets.xcassets来管理图片也可以达到ipa瘦身的效果~~

    话说PDFs矢量图呢 ,利用矢量图能不能帮助iOS App减少整体空间?

    iOS对矢量图的支持其实只是一种方便开发者的选择, 本质上在XCode编译的阶段矢量图会自动生成对应Target的@1x,@2x和@3x的png格式图像。在iOS实际运行中使用的图片实际上已经是png格式的图片了~

    用简单粗暴的实验来对比说明, 步骤如下:

    使用pdf原始文件编译生成通用IPA

    从生成的IPA文件中提取Asset.car文件

    利用iOS Image Extractor提取Asset.car文件

    将提取出来的@1x、@2x、@3x放置回工程, 并删除原始pdf中重新编译

    对比步骤1生成的car文件和步骤4生成的car文件大小

    结果如下:

    在iOS8.3以下, 相同压缩比例的条件下, 矢量图是无法帮助App减少空间。但是在iOS8.3以上, 利用xcassets可以避免多余的资源图片下载, 只下载对应的倍率的图片。因此, 严格意义下, 利用矢量图并不能帮助App节省空间(其实跟用Assets.xcassets的方式效果差不多)。但是pdf矢量图使用起来非常的方便, 建议使用。iOS本质上并不支持矢量图, 但是在编译阶段会将矢量图转化成目标设备对应的尺寸图, 同时会利用xcassets的特性在iOS8.3以上设备下支持部分资源下载, 带到包瘦身的效果。每次都要让UI给多个尺寸的图, 肯定没有给一张方便吧? 当然, 前提是UI的童鞋是基于矢量图工具制作的图片的前提下~

    简单的iOS瘦身技巧讲完了 ,我们来点儿稍微高级的

    (2).代码级别的优化

    比如 在项目里新建一个类,给它添加几个方法,但不要在任何地方import它,build完项目后观察linkmap,你会发现这个类还是被编译进可执行文件了。这是因为object-c的runtime 性质,按C++的经验,没有被使用到的类和方法编译器都会优化掉,不会编进最终的可执行文件,object-c不一样,因为object-c的动态特性,它可以通过类和方法名反射获得这个类和方法进行调用,所以就算在代码里某个类没被使用到,编译器也没法保证这个类不会在运行时通过反射去调用,所以只要是在项目里的文件,无论是否又被使用到都会被编译进可执行文件又比如我们的项目里会引入很多第三方静态库,如果能知道这些第三方库在可执行文件里占用的大小,就可以评估是否值得去找替代方案去掉这个第三方库。

    这个时候就要介绍一下LinkMap了,LinkMap文件是Xcode产生可执行文件的同时生成的链接信息,用来描述可执行文件的构造成分,包括代码段(__TEXT)和数据段(__DATA)的分布情况。比如说可执行文件的构成是怎样,里面的内容都是些什么, 

    1、使用LinkMap文件对可执行文件安装包进行分析

    在xcode的设置中 Project->Build Settings->Write Link Map File为YES,并设置Path to Link Map File,build完后就可以在设置的路径看到LinkMap文件了

    注意:此时最好使用真机进行编译,不然可能无法找到我们想要的文件。

    在以下目录可以看到LinkMap文件,如下:

    /Users/chenxintao/Library/Developer/Xcode/DerivedData/AppName-fnpgyspdoyxnotbpoliocmwypkff/Build/Intermediates/AppName.build/Debug-iphoneos/AppName.build/AppName-LinkMap-normal-arm64.txt

    LinkMap文件主要分为以下三部分:

    1.1 Object files

    整个可执行文件里包含的所有.O文件,前面的数字是这个.o文件的序号。样式如下:

    # Object files:

    [0] linker synthesized

    [1]/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk/usr/lib/crt1.o

    [2]/Users/bang/Library/Developer/Xcode/DerivedData/yishu-eyzgphknrrzpevagadjtwpzzeqag/Build/Intermediates/yishu.build/Debug-iphonesimulator/yishu.build/Objects-normal/i386/TKPFileInfo.o

    ...

    [280] /Users/bang/Downloads/yishu/yishu/Classes/lib/UMeng/MobClick/libMobClickLibrary.a(UMANJob.o)

    [281] /Users/bang/Downloads/yishu/yishu/Classes/lib/UMeng/MobClick/libMobClickLibrary.a(UMANWorker.o)

    [282] /Users/bang/Downloads/yishu/yishu/Classes/lib/UMeng/MobClick/libMobClickLibrary.a(MobClick.o)

    [283] /Users/bang/Downloads/yishu/yishu/Classes/lib/UMeng/MobClick/libMobClickLibrary.a(UMANLaunch.o)

    前面中括号里的是这个文件的编号,后面会用到,像项目里引用到静态链接库libMobClickLibrary.a里的目标文件都会在这里列出来。

    1.2 Sections

    接着是一个段表,描述各个段在最后编译成的可执行文件中的偏移位置及大小,包括了代码段(__TEXT,保存程序代码段编译后的机器码)和数据段(__DATA,保存变量值)。样式如下:

    # Sections:

    # Address  Size    Segment  Section

    0x00002740 0x00273890 __TEXT __text

    0x00275FD0 0x00000ADA __TEXT __symbol_stub

    0x00276AAC 0x00001222 __TEXT __stub_helper

    0x00277CCE 0x00019D9E __TEXT __objc_methname

    0x00291A70 0x00012847 __TEXT __cstring

    0x002A42B7 0x00001FC1 __TEXT __objc_classname

    0x002A6278 0x000046A7 __TEXT __objc_methtype

    0x002AA920 0x000061CE __TEXT __ustring

    0x002B0AF0 0x00000764 __TEXT __const

    0x002B1254 0x000028B8 __TEXT __gcc_except_tab

    0x002B3B0C 0x00004EBC __TEXT __unwind_info

    0x002B89C8 0x0003662C __TEXT __eh_frame

    0x002EF000 0x00000014 __DATA __program_vars

    0x002EF014 0x00000284 __DATA __nl_symbol_ptr

    0x002EF298 0x0000073C __DATA __la_symbol_ptr

    0x002EF9E0 0x000030A4 __DATA __const

    0x002F2A84 0x00000590 __DATA __objc_classlist

    0x002F3014 0x0000000C __DATA __objc_nlclslist

    0x002F3020 0x0000006C __DATA __objc_catlist

    0x002F308C 0x000000D8 __DATA __objc_protolist

    0x002F3164 0x00000008 __DATA __objc_imageinfo

    0x002F3170 0x0002BC80 __DATA __objc_const

    0x0031EDF0 0x00003A30 __DATA __objc_selrefs

    0x00322820 0x00000014 __DATA __objc_protorefs

    0x00322834 0x000006B8 __DATA __objc_classrefs

    0x00322EEC 0x00000394 __DATA __objc_superrefs

    0x00323280 0x000037C8 __DATA __objc_data

    0x00326A48 0x000096D0 __DATA __cfstring

    0x00330118 0x00001424 __DATA __objc_ivar

    0x00331540 0x00006080 __DATA __data

    0x003375C0 0x0000001C __DATA __common

    0x003375E0 0x000018E8 __DATA __bss

    1.3 Symbols

    可执行文件中各种symbol的大小,包括各个symbol的起始地址,占用大小,来自哪一个.o文件(使用之前提到的.o文件的序号)。样式如下:

    # Address   Size  File    Name

    0x00002740 0x0000003E [ 1] start

    0x00002780 0x00000400 [ 2] +[TKPFileInfo parseWithDictionary:]

    0x00002B80 0x00000030 [ 2] -[TKPFileInfo fileID]

    ...

    计算某个.o文件在最终安装包中占用的大小,主要是解析Object files和Symbols两个部分,从Object files读取出每个.o文件名和对应的序号,然后对Symbols中序号相同的文件的Size字段相加,即可得到每个.o文件在最终包的大小。 同样首列是数据在文件的偏移地址,第二列是占用大小,第三列是所属文件序号,对应上述Object files列表,最后是名字。例如第二行代表了文件序号为2(反查上面就是TKPFileInfo.o)的parseWithDictionary方法占用了1000byte大小。

    我们看到在这个里面除了可以看到DATA字段与TEXT字段的大小外,它还会列出所有类对象下的成员函数与类函数。其实这点很重要因为这样我们就可以知道工程中所有实现的函数了。通过相应的正则表达式,我们就可以提取出函数内容,其正则表达式为[+|-][.+\s(.+)],然后我们通过另外一个强大的反编译工具otool,可以提取出工程中所使用的函数列表(Used Selectors All)。

    先说那什么是otool呢?

    Otool可以提取并显示ios下目标文件的相关信息,包括头部,加载命令,各个段,共享库,动态库等等。它拥有大量的命令选项,是一个功能强大的分析工具,当然还可以做反汇编的工具使用。

    说到Otool就不得不提到mach-O ,那什么是mach-O? 

    Mach-O格式全称为Mach Object文件格式的缩写,是mac上可执行文件的格式,类似于windows上的PE格式 (Portable Executable ), linux上的elf格式 (Executable and Linking Format)。

    Mach-o包含三个基本区域:

    1.头部(header structure)。Mach-o的头部帮助内核迅速确定当前文件所支持的CPU架构。

    2.加载命令(load command)。

    3.段(segment)。可以拥有多个段(segment),每个段可以拥有零个或多个区域(section)。每一个段(segment)都拥有一段虚拟地址映射到进程的地址空间。

    4.链接信息。一个完整的用户级Mach-o文件的末端是链接信息。其中包含了动态加载器用来链接可执行文件或者依赖库所需使用的符号表,字符串表等等。 如下图左边就是苹果给出的mach-O格式的示意图 ,第二个图是我们使用machOView来分析某个可执行文件中的armv7的格式。可以看出他们两者的关系是对应的。


    我们是如何找出工程中所使用的函数列表的呢,其实就是使用命令字otool -V -s __DATA __objc_selrefs 项目.app/项目 | open -f。这里的项目地址指的是项目.app的路径地址,在Xcode7中的路径为

    /Users/用户名/Library/Developer/Xcode/DerivedData/项目名/Build/Products/Debug-iphonesimulator/项目名.app/项目名

    另外一个要注意的是-V要大写,因为大写和小写的命令是不一样的。当然大家也可以试试把DATA __objc_selrefs改成TEXT __objc_classname看看有什么不一样。

    下面就聊一聊如何对可执行文件进行瘦身。

    a.查找无用selector

    结合LinkMap文件的__TEXT.__text,通过正则表达式([+|-][.+\s(.+)]),我们可以提取当前可执行文件里所有objc类方法和实例方法(SelectorsAll)。再使用otool命令otool -v -s __DATA __objc_selrefs逆向__DATA.__objc_selrefs段,提取可执行文件里引用到的方法名(UsedSelectorsAll),我们可以大致分析出SelectorsAll里哪些方法是没有被引用的(SelectorsAll-UsedSelectorsAll)。注意,系统API的Protocol可能被列入无用方法名单里,如UITableViewDelegate的方法,我们只需要对这些Protocol里的方法加入白名单过滤即可。

    另外第三方库的无用selector也可以这样扫出来的。

    b. 查找无用oc类

    查找无用oc类有两种方式,一种是类似于查找无用资源,通过搜索"[ClassName alloc/new"、"ClassName *"、"[ClassName class]"等关键字在代码里是否出现。另一种是通过otool命令逆向__DATA.__objc_classlist段和__DATA.__objc_classrefs段来获取当前所有oc类和被引用的oc类,两个集合相减就是无用oc类。

    c.扫描重复代码

    可以利用第三方工具simian扫描(怎么使用自己去搜)。扫描重复代码,但维护成本过高,因为需要重构代码,没有删除代码来得直接(看自己的夜雾需求)。

    (3).编译选项优化

    这个最有用的一个选项是Strip Linked Product / Deployment Postprocessing / Symbols Hidden by Default 在release版本应该设为yes

    原理是打开这两个选项后构建ipa会去除掉symbol符号,就是那些类名啊函数名啊啥的。这样子的影响就是运行时你没法进行线程回溯,符号都没了回溯了也是乱码。但是不会影响正常的崩溃日志生成和解析。在本机专门测试过,如果使用符号表来解析崩溃日志,则完全不受影响。

    第二个 就是 Build Settings->Optimization Level有几个编译优化选项,release版应该选择Fastest, Smalllest,这个选项会开启那些不增加代码大小的全部优化,并让可执行文件尽可能小(不过默认就是如此)。

    展开全文
  • iOS 瘦身实践

    千次阅读 2018-06-28 21:54:39
    资源瘦身主要是去掉无用资源和压缩资源,资源包括图片、音视频文件、配置文件以及多语言wording。无用资源是指资源在工程文件里,但没有被代码引用。 检查方法思路:用资源关键字(通常是文件...

    前言

    App 的瘦身主要是针对于安装包,而在 iOS 中安装包就是一个以 .ipa 结尾的压缩包。我们可以通过 ipa 来分析,将ipa解压后可得到.app文件,右键可查看包内容(可执行文件、nib、storyboardc、car资源包等等)
    包瘦身,大致可以从以下几类入手:

    资源层面:
    Assets.car:项目中所有 .xcassets 的压缩包
    image: 图片资源文件
    Video && Audio :音频 或者 视频。
    Xib && Storyboard:Xib 和 Storyboard 编译后的文件。

    代码层面:
    项目可执行文件
    Frameworks:Embedded Frameworks,项目中使用的动态库

    一、资源瘦身

    资源瘦身我们可以分为远程资源(Remote)、本地资源(Local)
    Remote : 将资源文件放在服务器上,当用户下载完 App 后根据需要再下载。
    Local : 将资源文件集成到安装包中的。

    Remote
    我们可以把 非必须的资源文件 都放到服务器上,按需加载

    Local
    1.压缩大图片,通过用tinypng等工具来压缩,应该是以最小的占用量达到了最适合的效果。
    图片资源统一使用.xcassets 也会为你做一部分的压缩,查找大图:

    查找指定类型文件,超过10k的文件,并cp到指定目录下
     find ./ -type f -size +10k -name "*.png" -exec cp {}  /Users/xxx/Documents/png \;
    

    2.检查重复资源,这里指名字不同内容相同/相似,可以使用fdupes工具来扫描,在指定的目录及子目录中查找重复的文件。fdupes通过对比文件的MD5签名,以及逐字节比较文件来识别重复内容

    brew install fdupes
    fdupes -Sr 目录[ 目录...] > 输出文件.txt
    

    也可以使用UI工具gemini,
    官网下载:https://macpaw.com/gemini

    3.清理本地无用资源,无用资源是指资源在工程文件里,但没有被代码引用。通过去掉无用资源和压缩资源,资源包括图片、音视频等文件来优化。
    检查方法思路:用资源关键字(通常是文件名,图片资源需要去掉@2x @3x),搜索代码(一般是m\xib\sb文件),搜不到就是没有被引用。
    这里需要注意的是,如果资源是在xcassets中,其代码引用的资源名就不一定是图片名称,而是imageset后缀的文件夹名称
    当然,有些资源在使用过程中是拼接而成的(如loading_xxx.png),需要手工过滤,
    脚本实例(py):

    suffix = ".imageset"
    scanImagePath = "/Users/xxx/Documents/xxx/Pro/Images.xcassets" #扫描路径
    imageNamelist = []
    print("开始扫描【{0}】".format(scanImagePath))
    
    #找出所有资源名(imageset的资源都是目录,不是文件,取dirs)
    for root, dirs, files in os.walk(scanImagePath):
        for file in dirs:
            if file.endswith(suffix):
                imageName = file.replace(suffix,'')
                # imagePathDir = os.path.join(root, file)
                imageNamelist.append(imageName)
    
    #扫描代码文件
    scanTargetDir = "/Users/xxx/Documents/Pro"
    scanSuffix_m = ".m"
    scanSuffix_xib = ".xib"
    scanSuffix_sb = ".storyboard"
    invalidResList = []
    for name in imageNamelist :
        isExist = False
        for root, dirs, files in os.walk(scanTargetDir):
            for file in files:
                currentFilePath = os.path.join(root,file)
                if (not os.path.isdir(currentFilePath)) and (file.endswith(scanSuffix_m) or file.endswith(scanSuffix_xib) or file.endswith(scanSuffix_sb)):
                    try:
                        f = open(currentFilePath, "r")
                        fileContent = f.read()
                        isFind = fileContent.find(name,0,len(fileContent))
                        if isFind != -1:
                            isExist = True
                            break
                    except:
                        print ("读取失败%s",currentFilePath)
                    finally:
                        f.close()
            if isExist:
                break
    
        if not isExist:
            print ("找到无用资源:",name)
            invalidResList.append(name)
        
    output = open("result.txt", 'w') 
    output.write("{0}".format(invalidResList))   
    output.close()
    

    当然,你也可以用一下工具来扫,https://github.com/tinymind/LSUnusedResources

    二、可执行文件瘦身

    1.重复代码

    当一个项目在不断开发迭代、功能累加的过程中,因为业务轮转、新人加入等原因可能产生重复造轮子的问题,造成冗余代码。
    一般代码的重复检查,就是扫描代码中指定行数范围内是否有相同的代码。对于客户端代码而言,由于有iOS和Android两个平台,所以需要考虑工具的通用性,必须支持objective-C和java两种语言。
    基于以上原因,最后选择的工具是PMD-CPD(PMD’s Copy/Paste Detector)。此工具使用的是Karp-Rabin字符串匹配算法,支持gui,支持命令行,输出格式支持text、xml、csv等,可以很好的配合脚本语言进行二次开发,对重复率数据进行统计。

    1. 先从官网下载pmd工具包 https://sourceforge.net/projects/pmd/files/pmd/ 并解压
    2. cd进入其bin目录,执行./run.sh cpd --language ObjectiveC --minimum-tokens 120 --files /Users/xxx/Documents/项目目录

    ps:指定输出格式

    ./run.sh cpd  --language ObjectiveC --minimum-tokens 120 --format csv_with_linecount_per_file  --files /Users/xxx/Documents/项目目录 > codeCheck.csv
    

    使用./run.sh cpdgui启用gui界面工具

    详细参数用法可参考官网教程:https://pmd.sourceforge.io/pmd-5.5.1/usage/cpd-usage.html

    参数 说明
    cpd 重复代码扫描的批处理脚本
    –language ObjectiveC 指定语言为OC
    –minimum-tokens 100 指定被判定为重复代码的最少匹配的token数,数值100 ~ 150比较合适,越小则筛选强度越宽松
    –files 指定搜索文件目录
    > ~/Desktop/codeCheck.txt 将数据导出到 txt 文件

    PS:
    除此之外,还有很多其他检测工具如Simian

    2.查找无用方法

    LinkMap
    查找无用selector,首先先了解下LinkMap,LinkMap文件是Xcode产生可执行文件的同时生成的链接信息,用来描述可执行文件的构造成分,包括代码段(__TEXT)和数据段(__DATA)的分布情况。只要设置Project->Build Settings->Write Link Map File为YES,并设置Path to Link Map File,build完后就可以在设置的路径看到LinkMap文件了。
    每个LinkMap由3个部分组成:

    1. Object files:列举可执行文件里所有.obj文件,以及每个文件的编号,如 [ 1] /Users/luph/Library/Developer/Xcode/DerivedData/YYMobile-fpkgufbaoaunujctjgrwtzbylsll/Build/Intermediates.noindex/YYMobile.build/Debug-iphoneos/YYMobile.build/Objects-normal/arm64/YYBootingProtection.o
    2. Sections:是可执行文件的段表,描述各个段在可执行文件中的偏移位置和大小。第一列是段的偏移量,第二列是段占用大小,Address(n)=Address(n-1)+Size(n-1);第三列是段类型,代码段和数据段;第四列是段名字,如__text是可执行机器码,__cstring是字符串常量。如下:
    # Sections:
    # Address	Size    	Segment	Section
    0x100004FE0	0x03683FEC	__TEXT	__text
    0x103688FCC	0x0000EE74	__TEXT	__stubs
    0x103697E40	0x000043B0	__TEXT	__stub_helper
    0x10369C1F0	0x00004A08	__TEXT	__const
    0x1036A0BF8	0x00218EF0	__TEXT	__gcc_except_tab
    0x1038B9AE8	0x0001BB58	__TEXT	__ustring
    0x1038D5640	0x0007E908	__TEXT	__unwind_info
    0x103953F48	0x000000AC	__TEXT	__eh_frame
    0x103954000	0x00002280	__DATA	__got
    0x103956280	0x00009EF8	__DATA	__la_symbol_ptr
    0x103960178	0x00000840	__DATA	__mod_init_func
    0x1039609C0	0x000FD6E8	__DATA	__const
    0x103A5E0A8	0x00100C60	__DATA	__cfstring
    0x103B5ED08	0x0000F9C0	__DATA	__objc_classlist
    0x103B6E6C8	0x000001C8	__DATA	__objc_nlclslist
    
    1. Symbols:
      详细描述每个obj文件在每个段的分布情况,按第二部分Sections顺序展示,例如序号1的YYBootingProtection.o文件,+[YYBootingProtection isRepaired]方法在__TEXT.__text地址是0x100004FE0,占用大小是36字节。根据序号累加每个obj文件在每个段的占用大小,从而计算出每个obj文件在可执行文件的占用大小,进而算出每个静态库、每个功能模块代码占用大小。这里要注意的地方是,由于__DATA.__bbs是代表未初始化的静态变量,Size表示应用运行时占用的堆大小,并不占用可执行文件,所以计算obj占用大小时,要排除这个段的Size
    # Symbols:
    # Address	Size    	File  Name
    0x100004FE0	0x00000024	[  1] +[YYBootingProtection isRepaired]
    0x100005004	0x0000002C	[  1] +[YYBootingProtection setIsRepaired:]
    0x100005030	0x00000024	[  1] +[YYBootingProtection needForceUpdate]
    0x100005054	0x0000002C	[  1] +[YYBootingProtection setNeedForceUpdate:]
    0x100005080	0x00000024	[  1] +[YYBootingProtection isFixing]
    0x1000050A4	0x0000002C	[  1] +[YYBootingProtection setIsFixing:]
    

    无用方法检测思路
    以往C++在链接时,没有被用到的类和方法是不会编进可执行文件里。但Objctive-C不同,由于它的动态性,它可以通过类名和方法名获取这个类和方法进行调用,所以编译器会把项目里所有OC源文件编进可执行文件里,哪怕该类和方法没有被使用到。
    结合LinkMap文件的__TEXT.__text,通过正则表达式[+|-]\[\w+ \w+\],我们可以提取当前可执行文件里所有objc类方法和实例方法(SelectorsAll)。再使用otool命令otool -v -s __DATA __objc_selrefs逆向__DATA.__objc_selrefs段,提取可执行文件里引用到的方法名(UsedSelectorsAll),我们可以大致分析出SelectorsAll里哪些方法是没有被引用的(SelectorsAll-UsedSelectorsAll),
    扫描脚本(py):

    import os
    import re
    
    outPath = "/Users/luph/Documents/sizetj/" #输出目录
    mathoFilePaht = "/Users/luph/Documents/sizetj/Pro" #可执行文件
    linkmapPath = "/Users/luph/Documents/sizetj/Pro-LinkMap-normal-arm64.txt"
    selrefsFile =  outPath+"/selrefs.txt" #引用sel文件
    cmd = "otool -v -s __DATA __objc_selrefs "+ mathoFilePaht +" >> "+selrefsFile
    os.system(cmd) #逆向selrefs段
    
    linkmapContent = open(linkmapPath,encoding="utf8", errors='ignore').read()
    pattern = re.compile(r'[+|-]\[\w+ \w+\]') 
    selall = pattern.findall(linkmapContent)
    
    selrefsF = open(selrefsFile,encoding="utf8", errors='ignore')
    selrefsList = []
    for line in selrefsF.readlines():
        if '__objc_methname' in line:
            line = line.strip("\n");
            lineSplit = line.split(":")
            if  len(lineSplit)  > 0:
                selrefs = ""
                lineSplit.reverse()
                for subStr in lineSplit:
                    if len(subStr) > 0:
                        selrefs = subStr
                        break
                if len(selrefs) > 0:
                    selrefsList.append(selrefs)
    selrefsF.close()   
    
    output = open(outPath+"result.txt", 'w')
    for sel in selall:
        print("正在扫描【{0}】".format(sel))
        selMth = sel.replace("+",'')
        selMth = selMth.replace("-",'')
        selMth = selMth.replace("[",'')
        selMth = selMth.replace("]",'')
        selL = selMth.split(" ")
        selMth = selL[1]
        isUse = False
        for selref in selrefsList:
            if  selref == selMth:
                isUse = True
                break 
        if not isUse:
            print("发现无用方法【{0}】".format(sel))
            output.write("{0}\n".format(sel))  
         
    output.close()
    print("扫描结束")
    

    3.无用类

    使用fui工具扫描:
    https://github.com/dblock/fui

    https://github.com/flexih/Snake

    4.其他

    **语言选择:**不推荐使用 Swift,不论纯 Swift 还是 混编,任何一个包含有 Swift 代码的 App 都有的一个为了支持 Swift 的动态库集合,在10M 左右。如果你使用 Objective - C 完全不用这个东西

    三.更多相关

    1.App Thinning

    据Apple官方文档的介绍,App Thinning主要有三个机制

    Slicing

    开发者把App安装包上传到AppStore后,Apple服务会自动对安装包切割为不同的应用变体(App variant),当用户下载安装包时,系统会根据设备型号下载安装对应的单个应用变体。(你不需要做什么,iOS9.0.2以上就支持)
    在这里插入图片描述

    Bitcode

    开启Bitcode编译后,可以使得开发者上传App时只需上传Intermediate Representation(中间件),为二进制数据表示的格式的中间码,而非最终的可执行二进制文件。 在用户下载App之前,AppStore会自动编译中间件,产生设备所需的执行文件供用户下载安装。也就是当我们提交程序到 App Store上时, Xcode 会将程序编译为一个中间表现形式( bitcode )。然后 App store 会再将这个 Bitcode 编译为可执行的64位或32位程序。苹果会根据下载应用的用户的手机指令集类型生成只有该指令集的二进制,进行下发
    在这里插入图片描述

    所以,通过这个方式,我们可以做到架构级别的App Slicing。

    然而,一个很常见的误区是认为使用 bitcode 能优化包大小,其实启用 bitcode 作用并不大。实际上 bitcode 和包大小半毛钱关系都没有,它仅仅是把编译的最后一步留给苹果,这样苹果就可以在优化编译器后,再次将我们的应用打包,从而让历史应用也能享受到新技术
    https://www.appcoda.com/app-thinning/
    在文档里可看到
    In fact, app slicing handles the majority of the app thinning process. ‘App Slicing’ feature finally switched on in iOS 9.0.2
    说明slicing才是主要处理 app thinning的而且该功能需要在iOS9.0.2以上才支持(iOS9.0中被关闭了,因为一个iCloud的bug)。实际上Bitcode,做的事情是指令集优化。根据你设备的状态去做编译优化,进而提升性能。所以Bitcode对包的大小优化起不到什么本质上的作用。

    这就好比饭店原来是把菜做好了,等顾客来了以后直接上菜。现在厨师长说:“大家买好原材料”,万一哪天我们有了新的菜谱,同样的原材料就能做出更好吃的菜,用户就经常光顾我们这里了

    注意点
    1.开启 Bitcode 编译后,编译产生的 .app 体积会变大(中间代码,不是用户下载的包),且 .dSYM 文件不能用来崩溃日志的符号化(用户下载的包是 Apple 服务重新编译产生的,有产生新的符号文件)
    2.通过 Archive 方式上传 AppStore 的包,可以在Xcode的Organizer工具中下载对应安装包的新的dSYM符号文件。或者iTunes Connect上下载对应构建包的dSYM(需消除混淆)
    详情查看:https://developer.apple.com/library/archive/technotes/tn2151/_index.html

    On-Demand Resources

    ORD(随需资源)是指开发者对资源添加标签上传后,系统会根据App运行的情况,动态下载并加载所需资源,而在存储空间不足时,自动删除这类资源。
    这可能在游戏中应用场景会多一些。你可以用 tag 来组织像图像或者声音这样的资源,比如把它们标记为 level1,level2 这样。然后一开始只需要下载 level1 的内容,在玩的过程中再去下载 level2。或者也可以通过这个来推后下载那些需要内购才能获得的资源文件。
    在这里插入图片描述
    这种机制对于大多数APP来讲,看起来更像是按需加载网络图片,并作缓存处理。而On-Demand Resources只是将这个服务交由苹果来处理, 个人觉得多少显得鸡肋

    2.矢量图可行性分析

    PDF矢量图
    一开始,大家都以为,使用矢量图就可以不需要使用1x、2x、3x图了,毕竟人家不会有失真问题。那么,使用矢量图能不能帮助iOS App减少空间呢?
    试验:
    1.使用pdf原始文件编译生成通用IPA
    2.从生成的IPA文件中提取Asset.car文件
    3.利用iOS Image Extractor提取Asset.car文件

    可发现,解压后,除了PDF,还有对应的1-3x图,xcode并非直接使用PDF,而是以PDF大小为1x,生成了对应的2x、3x图,我们将解压出来的三张png图提取出来,重新打包ipa,结果对比如下:

    仅PDF 3张图 PDF大小
    115K 86KB 19KB

    结论:
    iOS对矢量图的支持其实只是一种方便开发者的选择, 本质上在XCode编译的阶段矢量图会自动生成对应Target的@1x,@2x和@3x的png格式图像,自动生成的@1x图会和矢量图的原始尺寸保持一致。在iOS实际运行中使用的图片实际上已经是png格式的图片了。所以,
    PDF矢量图对App减少空间是没什么实质上的帮助的,同时iOS9后,app Slicing的作用下,最终下载到手机的资源只有对应倍率的资源。因此, 严格意义下, 利用矢量图并不能帮助App节省空间,但从便利角度来说还是有好处的,设计不需要给开发多个尺寸的图,也就只有这点好处吧- -

    题外:
    这里解压car使用到了iOS Image Extractor工具,我们知道xcasset的格式应该是封闭不开放的, 该工具是怎么从Asset.car中提取图片的, 难道该工具破解了Asset.car的格式?
    通过浏览工程源码,我们发现 iOS Image Extrator其实是基于开源库iOS Asset Extrator开发实现的,核心提取的功能是在iOS Asset Extrator库下提取的, 笔者通过阅读其源码, 找到两个核心方法exportToDirectory:exportThemeRendition:
    通过阅读这两个方法的源代码可以了解到这个库的基本实现。exportToDirectory:方法有该库核心的提取图片的所有逻辑代码。而exportThemeRendition:可以看出该库支持的所有格式, 并且通过苹果内置的各个格式的Rendition类提取导出。
    iOS Asset Extrator库本质上调用的是苹果的私有API。在该系列API中, CUICommonAssetStorage负责存储Asset资源的关键key, CUICatalog是承载了具体资源图片信息的登记目录。
    开源库底层既然是苹果API, 那么就基本是一个黑盒子了。既不能从暴露的API中分析出car的格式, 又不能判断iOS设备是否在执行中解压, 只好放弃~
    PS:Xcode 默认自动使用 PNGCRUSH 压缩 .png 图片

    by2020-10-27更新:
    对于car,我们还可以通过assetutil工具来获取图片的大小信息,这个对于进行资源对比非常有意义。
    通过运行xcrun命令

    sudo xcrun --sdk iphoneos assetutil --info /Users/Downloads/Payload/Mobile.app/Assets.car
    

    可获取car的配置json文件,例如:

    {
        "Height" : 60,
        "Scale" : 1,
        "RenditionName" : "D3801CE9-19F1-4CE9-97C6-7E1EFFFCAE89",
        "AssetType" : "Image",
        "SizeOnDisk" : 10822,
        "Name" : "mailbox",
        "Idiom" : "universal",
        "Width" : 99
      },
    

    其中
    AssetType,表示该资源类型
    RenditionName,表示图片资源的名称
    Name,代表该资源对应的imageset名称
    SizeOnDisk,表示该图片在car中占用多大的字节空间

    最后再说点:
    一般包体对比需要统一口径,在Appstore下appthinning的存在,我们一般打出来的通用包体积都较大,如果要看该ipa不同设备的变体大小,可使用apple的方式查看:
    https://developer.apple.com/cn/documentation/xcode/reducing_your_app_s_size/

    摘录:
    http://blog.startry.com/2016/06/15/vector-apply-to-iOS-Project/
    https://juejin.im/post/5800ef71a0bb9f0058736caa

    展开全文
  • iOS 瘦身策略

    2020-05-20 15:27:32
    1、生成linkmap文件,查看可执行文件的大小。在Build Settings中Write Link Map File设置为Yes(release时候不要设置为Yes),build后,LinkMap文件ingage-LinkMap-normal-x86_64,地址:/Users/XXX/Library/Developer/...

    1、生成linkmap文件,查看可执行文件的大小。在Build Settings中Write Link Map File设置为Yes(release时候不要设置为Yes),build后,LinkMap文件ingage-LinkMap-normal-x86_64,地址:/Users/XXX/Library/Developer/Xcode/DerivedData/XXX-aquckyasacajztamzgvydtrnnqtc/Build/Intermediates.noindex/XXX.build/Debug-iphonesimulator/ingage.build。

    LinkMap分析工具:https://github.com/huanxsd/LinkMap

    2、无用图片资源的删除。

    工具:LSUnusedResources(https://github.com/tinymind/LSUnusedResources

    3、图片压缩

        工具使用ImageOptim(https://github.com/ImageOptim/ImageOptim)。当然小熊猫tinypng这个压缩也非常好用(https://tinypng.com/

    4、删除无用文件

    工具:CATClearProjectTool-master(https://github.com/CatchZeng/CATClearProjectTool

    5、打包策略调整,编译器优化级别。Xcode中,Build Settings->Optimization Level,release版应该选择Fastest, Smalllest,这个选项会开启那些不增加代码大小的全部优化,并让可执行文件尽可能小。

    6、去除符号信息。Xcode中,Strip Debug Symbols During Copy 和 Symbols Hidden by Default 在release版本应该设为yes,可以去除不必要的调试符号。

    展开全文
  • iOS瘦身实践

    2018-04-24 17:22:45
    资源级的瘦身 使用LSUnusedResources删除无用图片。注意只是简单的删除。需要自己在此确认。 使用WebP代替PNG, 转换及压缩工具isparta。 WebP的优点: Webp 压缩率⾼,⽀持有损与⽆损压缩 WebP 体积⼤幅减少,⾁...

    资源级的瘦身

    • 使用LSUnusedResources删除无用图片。注意只是简单的删除。需要自己在此确认。
    • 使用WebP代替PNG, 转换及压缩工具isparta
    • WebP的优点:
      1. Webp 压缩率⾼,⽀持有损与⽆损压缩
      2. WebP 体积⼤幅减少,⾁眼看不出差异
        缺点:Webp更加消耗性能,较PNG消耗2倍左右的CPU和解码时间

    代码级的瘦身-linkmap

    首先需要了解linkmap是什么?LinkMap文件是Xcode产生可执行文件的同时生成的链接信息,用来描述可执行文件的构造成分,包括代码段(__TEXT)和数据段(__DATA)的分布情况。比如说可执行文件的构成是怎样,里面的内容都是些什么,

    • 默认xcode在debug下是不生成linkmap文件的,所以我们首先要设置在debug模式下开启生成linkmap,在build setting中搜索link map,设置write link map file为YES,再次运行可以可以在 path to link map file中找到对应生成的text文件。以为自己的地址举例:/Users/用户名/Library/Developer/Xcode/DerivedData/项目名称-hhqqxhkyzdufzbfyuefaytyuxebf/Build/Intermediates.noindex/项目名称.build/Debug-iphoneos/项目名称.build/项目名称-LinkMap-normal-arm64.txt其中项目名称后面的随机字符串可能不一致。

    • 分析生成的link map中的执行文件大小。WMLinkMapAnalyzer。可以查看静态库是否过大。如果过大可以参考lipo -info libWeChatSDK.a Architectures in the fat file: libWeChatSDK.a are: armv7 armv7s i386 x86_64 arm64
      i386,x86_64,是模拟器的指令集,如果不考虑模拟器运行,可以删除。

    使用下面脚本或者使用appcode的code inspect 来检查未使用的类和方法
    // 查找无用的类脚本

    # 使用方法:python py文件 Xcode工程文件目录
    
    # -*- coding:UTF-8 -*-
    import sys
    import os
    import re
    
    if len(sys.argv) == 1:
        print '请在.py文件后面输入工程路径' 
        sys.exit()
    
    projectPath = sys.argv[1]
    print '工程路径为%s' % projectPath
    
    resourcefile = []
    totalClass = set([])
    unusedFile = []
    pbxprojFile = []
    
    def Getallfile(rootDir): 
        for lists in os.listdir(rootDir): 
            path = os.path.join(rootDir, lists) 
            if os.path.isdir(path): 
                Getallfile(path) 
            else:
                ex = os.path.splitext(path)[1]  
                if ex == '.m' or ex == '.mm' or ex == '.h':
                    resourcefile.append(path)
                elif ex == '.pbxproj':
                    pbxprojFile.append(path)
    
    Getallfile(projectPath)
    
    print '工程中所使用的类列表为:'
    for ff in resourcefile:
        print ff
    
    for e in pbxprojFile:
        f = open(e, 'r')
        content = f.read()
        array = re.findall(r'\s+([\w,\+]+\.[h,m]{1,2})\s+',content)
        see = set(array)
        totalClass = totalClass|see
        f.close()
    
    print '工程中所引用的.h与.m及.mm文件'
    for x in totalClass:
        print x
    print '--------------------------'
    
    for x in resourcefile:
        ex = os.path.splitext(x)[1]
        if ex == '.h': #.h头文件可以不用检查
            continue
        fileName = os.path.split(x)[1]
        print fileName
        if fileName not in totalClass:
            unusedFile.append(x)
    
    for x in unusedFile:
        resourcefile.remove(x)
    
    print '未引用到工程的文件列表为:'
    
    writeFile = []
    for unImport in unusedFile:
        ss = '未引用到工程的文件:%s\n' % unImport
        writeFile.append(ss)
        print unImport
    
    unusedFile = []
    
    allClassDic = {}
    
    for x in resourcefile:
        f = open(x,'r')
        content = f.read()
        array = re.findall(r'@interface\s+([\w,\+]+)\s+:',content)
        for xx in array:
            allClassDic[xx] = x
        f.close()
    
    print '所有类及其路径:'
    for x in allClassDic.keys():
        print x,':',allClassDic[x]
    
    def checkClass(path,className):
        f = open(path,'r')
        content = f.read()
        if os.path.splitext(path)[1] == '.h':
            match = re.search(r':\s+(%s)\s+' % className,content)
        else:
            match = re.search(r'(%s)\s+\w+' % className,content)
        f.close()
        if match:
            return True
    
    ivanyuan = 0
    totalIvanyuan = len(allClassDic.keys())
    
    for key in allClassDic.keys():
        path = allClassDic[key]
    
        index = resourcefile.index(path)
        count = len(resourcefile)
    
        used = False
    
        offset = 1
        ivanyuan += 1
        print '完成',ivanyuan,'共:',totalIvanyuan,'path:%s'%path
    
    
        while index+offset < count or index-offset > 0:
            if index+offset < count:
                subPath = resourcefile[index+offset]
                if checkClass(subPath,key):
                    used = True
                    break
            if index - offset > 0:
                subPath = resourcefile[index-offset]
                if checkClass(subPath,key):
                    used = True
                    break
            offset += 1
    
        if not used:
            str = '未使用的类:%s 文件路径:%s\n' %(key,path)
            unusedFile.append(str)
            writeFile.append(str)
    
    for p in unusedFile:
        print '未使用的类:%s' % p
    
    filePath = os.path.split(projectPath)[0]
    writePath = '%s/未使用的类.txt' % filePath
    f = open(writePath,'w+')
    f.writelines(writeFile)
    f.close()

    以上查找无用类的方法只能作为参考。还需要人工筛选一遍。很多通过xib创建、或者cell通过register方法创建的。脚本是无法区分的。所以还需人工审查一遍。但也已经大大减少了很多繁琐的工作量了。

    编译器级别瘦身。

    • 这个最有用的一个选项是Deployment Postprocessing / Strip Linked Product / Symbols Hidden by Default 在release版本应该设为yes

    更多的瘦身方法

    参考滴滴出行iOS端瘦身实践

    个人总结:项目尝试过上述几种方法,Xcode最终打出ipa包由原来的63M缩小到了50M。因为项目原因,个人感受这边最为行之有效的应该算是资源的压缩。可能根据不同项目的场景各有不同。大家都可以进行尝试!

    参考:

    滴滴出行iOS端瘦身实践

    当我们谈论iOS瘦身的时候,我们到底在谈论些什么

    展开全文
  • 1.iOS瘦身之armv7 armv7s arm64选用 机器对指令集的支持是向下兼容的,因此armv7的指令集是可以运行在iphone5S以上的,只是效率没那么高而已~ 但是由于苹果要求必须支持arm64,因此这个架构是必须存在的.在xcode...
  • iOS 瘦身之道

    2019-05-16 09:09:31
    App 的包大小做优化的目的就是为了节省用户流量,提高用户的下载速度,也是为了用户手机节省更多的空间。另外 App Store 官方规定 App 安装包如果超过...同时如果你的 App 需要适配 iOS7、iOS8 那么官方规定主二进制...
  • 源码LinkMapAnalyzer,这个文件可以让你了解整个APP编译后的情况,也许从中可以发现一些异常,还可以用这个文件计算静态链接库在项目里占的大小,有时候我们在项目里链了很多第三方库,导致APP体积变大很多,我们想...
  • iOS瘦身之删除FrameWork中无用mach-O文件 原文 http://www.infoq.com/cn/articles/ios-thinning-delete-unnecessary-mach-o 最近项目末期, 我们团队为了ipa的大小使用不少的体积减小的方法, 除了...
  • 1: #import <Foundation/Foundation.h> typedef void(^configureCellBlock)(id cell, id item);//瘦身viewcontroller @interface RRFriendTableViewDataSource : NSObject<UITableViewDa...
  • App 的包大小做优化的目的就是为了节省用户流量,提高用户的下载速度,也是为了用户手机节省更多的空间。另外 App Store 官方规定 App 安装包如果...同时如果你的 App 需要适配 iOS7、iOS8 那么官方规定主二进制 ...
  • 【IOS】IOS瘦身之armv7 armv7s arm64选用

    千次阅读 2018-02-22 09:14:24
    于是就开始了各种瘦身计划:压缩图片,删除不必要静态库。做了一番改动后发现包只比原来小了1M多,收效甚微。后来经过查阅资料才知道armv6 armv7 armv7s arm64引起编译包翻倍增大的问题,于是就试了一下,果然ipa包...
  • iOS瘦身-无用Pod依赖检查脚本

    千次阅读 2017-04-19 16:23:59
    最近在研究 iOS瘦身、提升编译速度的策略。 无用 Pod 依赖,是容易被忽略的一个可优化点,去掉无用依赖,一方面可以降低包大小,另一方面也能加快编译速度。 然鹅!!!这么一个朴素而常见的需求,搜遍全网却没有...
  • iOS瘦身之路(上)

    2017-10-28 09:54:21
    奋斗在给自己瘦身 和 给app瘦身的道路上。 主要参考了这篇博客:http://www.jianshu.com/p/6422a6861562 主要步骤:1:将项目中的第三方字体去掉,项目的基本框架最好用系统自带的字体,如果列表展示等需要...
  • 对于支持iOS 9.0及以后的app,按需加载资源是默认开启的。你也可以在target的build settings中手动更改。   启用或关闭按需加载资源: 1、在project navigator中选择工程文件。 2、在...
  • 滴滴出行iOS端瘦身实践,iOS瘦身时间指南,iOSPDF,iOS
  • iOS SDK 瘦身技巧

    2017-07-13 12:02:42
    最近开发的SDK在android 和iOS 不同平台的体积大小差异较大,网上搜集了一些iOS瘦身的设置,现总结一下,欢迎补充。 最有效的瘦身 1.Generate Debug Symbls 为NO ,我的项目由90->10M. 2.设置Optimization Level下...
  • iOS应用瘦身

    2019-04-23 15:19:12
    iOS应用瘦身 iOS9新特性,根据用户设备型号,在保证应用程序完整的情况下,尽可能的压缩和减少应用程序安装包的体积,实现方法主要有三种:应用程序切片(Slicing)
  • iOS 安装包瘦身的方法

    2018-02-11 13:17:17
    讲述iOS 安装包瘦身的有效的解决方案,为iOS程序开发者提供有效的帮助
  • iOS瘦身

    2018-07-30 14:36:10
    iOS瘦身我的思路在于以下几点: 资源文件优化:资源文件包括图片、gif、音频、文本、配置文件、xib、storyboard等。 对.o(可执行)文件优化:.o文件由我们编写的代码.h和.m(源文件)编译后生成。 设置Xcode的...
  • iOS app 瘦身

    2017-05-27 11:41:01
    本文译自:Guides and Sample Code 的App Thinning (iOS, tvOS, watchOS)App 瘦身App Store和操作系统通过将app定制到用户的特定设备中来优化安装, 这种瘦身优化,方便了我们更快地下载app,为我们的设备节省空间,...
  • iOS 安装包瘦身

    2019-08-02 16:14:08
    缩减iOS安装包大小是很多中大型APP都要考虑的问题,一般首先会对资源文件进行处理,压缩图片和音频资源,去除不必要的资源。这些资源优化做完后,我们还可以尝试对可执行文件进行瘦身,项目越大,可执行文件占用的...
  • iOS安装包瘦身

    千次阅读 2016-10-06 14:59:00
    安装包主要由两部分组成,资源文件以及可执行文件,瘦身主要从这两部分入手; 【1】资源文件瘦身 资源文件包括图片、声音、视频、文本文件等,只要在xcode中引用就会被打包到安装包中。  1.检查项目中有哪些...
  • ios app 瘦身

    2017-02-17 17:05:27
    APP包瘦身 1)删除项目中没有使用的图片。 查找XCode工程中没被使用的图片资源 2)删除项目中没有用到的class 和 xib 及storyboard。分析工程中没有用到class 安装 fui 工具 sudo gem install fui -n /...
  • GMTC滴滴出行iOS瘦身实践,方便iOS开发实践减少iOS开发包体积

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 530
精华内容 212
关键字:

ios瘦身