iOS - ipa安装包大小优化

在App Store上显示的下载大小和实际下载下来的大小,我们通过下表做一个对比:


iPhone型号

系统

AppStore 显示大小

下载到设备大小
iPhone6 10.2.1 91.5MB 88.9MB
iPhone6 10.1.1 91.5MB 88.9MB
iPhone6 9.3.5 91.5MB 84.8MB
iPhone 5 9.2 91.5MB 84.8MB
iPhone6 plus 10.0.2 95.7MB 93.2MB
iPhone7 plus 10.3.0 95.7MB 93.2MB
iPhone5C 9.2 83.9MB 76MB
iPhone5S 7.1.1 147MB 144MB
iPhone5C 7.1.2 147MB 未知
iPhone5C 越狱 8.1.1 83.9MB 144MB

从上表可以看到:

  • 在 iOS 9 系统以上的手机上,App Store 上的大小都是做了 App Thinning 操作的。
  • 在 iOS 9 以上系统的基础上,plus 手机在 AppStore size 上比其他手机大了 4.2MB,猜测是因为 @3x 图的原因。
  • iOS 9 和 iOS 10 虽然在  AppStore 显示的包大小一致,但是最终下载到手机上,大小有区别。
  • iOS 9 以下的手机,是直接下载整个安装包的。

App Thinning】:对于iOS应用来说,应用瘦身仅支持最新版本的iTunes,以及运行iOS 9.0或者更高系统的设备,否则的话,App Store将会为用户分发统一的安装包。iOS 9 在发布时隐含一个 Bug , App Thinning ( App 瘦身)无法正确运作。随着 iOS 9.0.2 的发布,此 Bug 已被修复, App 瘦身终于可以运作如常。从 App Store 下载 App 时请谨记这点。App Thinning 会自动检测用户的设备类型(即型号名称)并且只下载当前设备所适用的内容。换句话说,如果使用的是 iPad Mini 1(1x分辨率且非 retina 显示屏)那么只会下载 1x分辨率所使用的文件。更强大和更高分辨率的 ipad(如iPad Mini 3或 4)所使用的资源将不会被下载。因为用户仅需下载自己当前使用的特定设备所需的内容,这不仅加快了下载速度,还节约了设备的存储空间。

苹果建议删除一些无用的执行代码或资源文件。下面我们分别从这两方面来分析安装包瘦身的一些方法和工具。

1.资源文件

资源文件包括图片、声音、配置文件、文本文件(例如rtf文件)、xib(在安装包中后缀名为nib)、storyboard等。对于声音、配置文件、文本文件这三类资源文件,一般在安装包中数量不多,可自行在工程中根据实际情况,进行删除或保留。声音文件过大的话,可以考虑用如下命令做压缩:

//tritone.caf为声音文件
afconvert -f AIFC -d ima4 tritone.caf

xib和storyboard文件实际上是一个xml文件,如果某个页面没有使用,可直接删除。这里主要说一下对图片资源的处理方式。

对图片资源类文件,一般采取的方法是这几种:

  • 删除无用的资源文件;
  • 对资源文件进行压缩;
  • 变更图片文件的导入方式;
  • 处理1x图片。

1.1删除无用的资源文件

在这里推荐使用工具LSUnusedResources。它在脚本的基础上,做了两个改进:

  • 提高匹配速度。LSUnusedResources不是对每个资源文件名都做一次全文搜索匹配,因为加入项目的资源太多,这里会导致性能快速下降。它只是针对源码、Xib、Storyboard 和 plist 等文件,先全文搜索其中可能是引用了资源的字符串,然后用资源名和字符串做匹配。
  • 优化匹配结果。比如说脚本会把大量实际上有使用的资源,当做未使用的资源输出(例如拼接的图片名称),而LSUnusedResources不会。

接下来,打开工具LSUnusedResources,点击“Browse...”按钮,选择工程所在目录,点击"Search"按钮,即可开始搜索,如下图所示:

搜索结果出来之后,选中某行,点击“Delete”按钮即可直接删除资源。

1.2对资源文件进行压缩

压缩工具有很多,这里介绍两个好用的:

  • 无损压缩工具ImageOptiom(推荐)。这是一款非常好的图片压缩工具,可以进行无损压缩,能够对 png 和 jpeg 图片文件进行优化,它能找到最佳的压缩参数(在设置中可以设置压缩比例,80% 及以上是无损压缩,推荐使用),并通过消除不必要的信息(如文件的 EXIF 标签和颜色配置文件等),优化后达到减小文件大小的效果。
  • 有损压缩工具TinyPNG。它使用聪明的有损压缩技术,能有效减少PNG文件的大小。通过选择性地降低图像中颜色的数量,需要更少的字节来存储数据。

【建议】:对于较大尺寸的图片,可以和设计沟通,在不失真和影响效果的前提下,使用TinyPNG进行压缩;较小尺寸的图片,建议使用ImageOptiom

1.3变更图片文件的导入方式

我们都知道,图片资源的导入方式有如下几种:

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文件夹

 打包后的Assets.car文件夹
 第一次
32.7MB


16.3MB

第二次  33.5MB  26.1MB

从表格数据可以看到,使用Assets.xcassets来管理图片也可以达到ipa瘦身的效果

值得留意的是,在将图片资源移到Assets.xcassets管理的时候,一般情况下会自动生成与图片名称相同的,比如[email protected]和[email protected]会自动放置到一个同名的loading文件夹中。然而有一些不规则命名的图片,会出现一些奇怪的问题:

  • 图片名称为ios-f2-8-004的图片,放到Images.xcassets中,会自动生成调用的图片名是ios-f2-8-4,最后一位的004,被替换成4,然而在类文件中引用的是[UIImage imageNamed:@"ios-f2-8-004.png"],这样会找不到图片;
  • 图片名称为ios-f6-的图片,放到Images.xcassets中,会自动生成调用的图片名是ios-f6,这样也会找不到图片。

因此在移动的时候,一定要细致对比。

1.4处理1x图片

我们知道,iPhone设备目前主要有四种尺寸:3.5英寸、4英寸、4.7英寸、5.5英寸,对于这几个尺寸的设备,我们来看一下具体的设备型号和屏幕相关信息:

机型 屏幕宽高(point) 渲染像素(pixel) 物理像素(pixel) 屏幕对角线长度(英寸) 屏幕模式
iPhone 2G, 3G, 3GS 320 * 480 320 * 480 320 * 480 3.5(163PPI) 1x
iPhone 4, 4s 320 * 480 640 * 960 640 * 960 3.5 (326PPI) 2x
iPhone 5, 5s 320 * 568 640 * 1136 640 * 1136 4 (326PPI) 2x
iPhone 6, 6s, 7 375 * 667 750 * 1334 750 * 1334 4.7 (326PPI) 2x
iPhone 6 Plus, 6s Plus, 7 Plus 414 * 736 1242 * 2208 1080 * 1920 5.5 (401PPI) 3x

对于上表中的几个概念,这里做一下说明:

  • Points: 是iOS开发中引入的抽象单位,称作点。开发过程中所有基于坐标系的绘制都是以 point 作为单位,在iPhone 2G,3G,3GS的年代,point 和屏幕上的像素是完全一一对应的,即 320 * 480 (points), 也是 320 * 480 (pixels);
  • Rendered Pixels: 渲染像素, 以 point 为单位的绘制最终都会渲染成 pixels,这个过程被称为光栅化。基于 point 的坐标系乘以比例因子可以得到基于像素的坐标系,高比例因子会使更多的细节展示,目前的比例因子会是 1x,2x,3x
  • Physical Pixels: 物理像素,就是设备屏幕实际的像素。
  • Physical Device: 设备屏幕的物理长度,使用英寸作为单位。比如iPhone 4屏幕是3.5英寸,iPhone 5 是4英寸,iphone 6是4.7英寸,这里的数字是指手机屏幕对角线的物理长度。实际上会是Physical Pixels的像素值(而不是Rendered Pixels的像素值)会渲染到该屏幕上, 屏幕会有 PPI(pixels-per-inch) 的特性,PPI 的值告诉你每英寸会有多少像素渲染。
  • 屏幕模式: 描述的是屏幕中一个点有多少个 Rendered Pixels 渲染,对于2倍屏(又称 Retina 显示屏),会有 2 * 2 = 4 个像素的面积渲染,对于3倍屏(又称 Retina HD 显示屏),会有 3 * 3 = 9 个像素的面积渲染。

在实际的开发中,所有控件的坐标以及控件大小都是以点为单位的,假如屏幕上需要展示一张 20 * 20 (单位:point)大小的图片,那么设计师应该怎么给图呢?这里就会用到屏幕模式的概念,如果屏幕是 2x,那么就需要提供 40 * 40 (单位: pixel)大小的图片,如果屏幕是 3x,那么就提供 60 * 60 大小的图片,且图片的命名需要遵守以下规范:

  • Standard: <ImageName><device_modifier>.<filename_extension>
  • High resolution: <ImageName>@2x<device_modifier>.<filename_extension>
  • High HD resolution: <ImageName>@3x<device_modifier>.<filename_extension>

其中:

  • ImageName: 图片名字,根据场景命名
  • device_modifier: 可选,可以是 ~ipad 或者 ~iphone, 当需要为 iPad 和 iPhone 分别指定一套图时需要加上此字段
  • filename_extension: 图片后缀名,iOS中使用 png 图片

2x屏幕的设备会自动加载 [email protected] 命名的图片资源,3x屏幕的设备会自动加载 [email protected] 的图片。从友盟统计数据可以看到,现在基本没有 1x屏幕的设备了,所以可以不用提供这个分辨率的图片

至于开发中,技术人员和设计人员关于设计和切图的工作流程和规范,可以参看知乎上的这篇文章介绍。

2.1清理无用代码--AppCode

AppCode是一种智能的Objective-C集成开发环境,由专业的开发收费IDE的公司Jetbrains开发,具有这些特点:

  • 最好的代码助手:IDE深入的了解代码结构,编辑器能提供准确的代码实现选择。通过代码生成节省了不必要的输入,减少了日常任务。
  • 可靠的代码重构:安全、准确和可靠的代码重构允许我们随时修改和提升代码质量。
  • 快速项目导航:通过类继承可以从方法导航到它的声明或使用处,或者直接从一个文件链接到另一个文件。支持即时跳转到项目中的任何文件、类、标号处,或者查看标号的实际使用者,并不仅仅是文本匹配那么简单。
  • 代码质量追踪:支持对Objective-C、C、C++、JavaScript、CSS、HTML、XML和Xpath等进行动态代码分析。AppCode能让您避免潜在的错误,提示您哪些代码可以改善。此外,它还集成了Clang Static Analyzer。
  • 强大的代码调试器:使用便携调试器中灵活的断点、窗口、框架视图和求值表达式调整您的应用或单元测试。
  • 无缝集成:AppCode完美地集成大部分流行的版本控制系统,如Git, Mercurial、Perforce等,还集成了Kiwi测试框架、Dash和成分文档工具以及很多问题追踪器,提供与Xcode100%的互操作性。

在这里,我们可以用它的inspect code来扫描无用代码,包括无用的类、函数、宏定义、value、属性等,而safe delete功能使得删除一些由于runtime被调用到的代码时更加安全智能。扫描结果示例:

【说明】:如果工程很大,这个扫描的时间可能会比较长。我们现在的工程中,大概有2700个类,扫描时间在一个半小时。

2.2清理无用类

实际上,在2.1的扫描结果中,包含无用类,但2.1的扫描时间会比较长,另外扫描出来的内容也较多。如果只是需要清理无用类的话,可以用如下脚本:

# -*- coding: UTF-8 -*-
#!/usr/bin/env python
# 使用方法:python py文件 Xcode工程文件目录

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

同样的工程,这个脚本执行速度大概是三分钟,结果如下:

原文地址:https://www.cnblogs.com/gongyuhonglou/p/8985208.html

时间: 2024-07-29 09:06:52

iOS - ipa安装包大小优化的相关文章

Xcode如何打包ipa安装包以及出现的证书无效解决方案

生成ipa安装包,项目运行设备选择IOS Device ,然后再选择Product,根据下列图示进行操作 准备工作 在这里选择Debug和Release安装包的时候请注意这里是有区别的,然后我们进行下一步 弹出对话框,之后等等待几秒钟出现如下操作框.选择"Distribute..."弹出如下提示框,选择第二项,点击"Next"弹出的界面中选择和第2步中相同的证书,点击"Next",等待几秒,弹出保存界面设置包名称,点击"Save&quo

【转】利用xcode生成的app生成可以在iphone和itouch上运行的ipa安装包

转载地址:http://blog.csdn.net/yohunl/article/details/5971252 在编译好的真机版目录下的.app文件,至于生成真机可以运行的app的方法,有两种方式,一种是交99美元获得一个证书,另外一种是破解的方式,在此不再详述,本文假设你已经生成了真机上可以运行的app包了(app包实际上是一个文件夹) 假设此安装包的名称是 hello.app,点击右键,选择 显示包内容,这样就可以打开这个hello.app文件夹了,在此文件夹中有一个info.plist文

利用xcode生成的app生成可以在iphone和itouch上运行的ipa安装包

在编译好的真机版目录下的.app文件,至于生成真机可以运行的app的方法,有两种方式,一种是交99美元获得一个证书,另外一种是破解的方式,在此不再详述,本文假设你已经生成了真机上可以运行的app包了(app包实际上是一个文件夹) 假设此安装包的名称是 hello.app,点击右键,选择 显示包内容,这样就可以打开这个hello.app文件夹了,在此文件夹中有一个info.plist文件,打开它,新增加一个名为 SignerIdentity的key字段,内容为Apple iPhone OS App

iOS打包ipa安装包的流程

应用的发布也分两种 一种是.打包成ipa上传到国内第3方软件市场,当用户的手机已经JailBreak时,双击下载的ipa文件就可以安装软件 (ipa同android的apk包一样,实质是一个压缩包) 方式一:使用XCode打包ipa 1.选择 Edit Scheme 2.选择图中显示的[Release] 注意,此时的工程左边Products目录下仍然是红色的<??"http://www.2cto.com/kf/ware/vc/" target="_blank"

xcode工程命令行生成ipa安装包

主要使用两个命令 xcodebuild:将xcode工程打包成.app文件 xcrun:将app打包成.ipa文件 下面是写好的ant脚本: 1 <?xml version="1.0" encoding="UTF-8"?> 2 <project default="release" name="PackageForIOS" basedir="."> 3 <property nam

Unity3D游戏开发之如何减少安装包大小

第一步要做的就是:看看哪些文件是最占空间的,那么它们就是首选优化对象了. 你可以在刚刚完成一次build之后在"Editor Log"中找到这些信息. 如何打开Editor Log: 在Mac上看起来就是这样的了: 可以看出这份log提供了一份资源总括:各种类型资源的总大小,以及所占百分比.同时还降序列出了单个文件的大小. 更多精彩请点击[狗刨学习网] 顺带一提,资源类型中的"File headers"它们并不是资源本身,而是加在原始资源上,用来存储"引用

Unity3D如何减少安装包大小

译官方文档:http://docs.unity3d.com/Manual/ReducingFilesize.html PDF文档:http://www.rukawa.cn/Uploads/Attachment/ReducingFilesize/ReducingFilesize.pdf 原文地址:http://www.rukawa.cn/index.php?s=/home/article/detail/id/27.html 需要这么做的目的和好处就不多说了. 第一步要做的就是:看看哪些文件是最占空

生成Ipa安装包的plist文件后生成下载链接

假设生成的plist文件的下载链接是: https://www.xx.com/download/xx.plist 那么如果想让苹果手机的浏览器点击后开始下载苹果软件包,则网页中的下载链接需要拼接成 itms-services://?action=download-manifest&url=https://www.xx.com/download/xx.plist 今天是难忘的一天 原文地址:https://www.cnblogs.com/Stay-J/p/9691655.html

iOS安装包瘦身的那些事儿

在我们提交安装包到App Store的时候,如果安装包过大,有可能会收到类似如下内容的一封邮件: 收到这封邮件的时候,意味着安装包在App Store上下载的时候,有的设备下载的安装包大小会超过100M.对于超过100M的安装包,只能在WIFI环境下下载,不能直接通过4G网络进行下载. 在这里,我们提交App Store的安装包大小为67.6MB,在App Store上显示的下载大小和实际下载下来的大小,我们通过下表做一个对比: iPhone型号 系统 AppStore 显示大小 下载到设备大小