app在3.0版本時(shí)安裝包已達(dá)到66.1M,app瘦身刻不容緩益眉。 App安裝包是由資源和可執(zhí)行文件兩部分組成,安裝包瘦身也是從這兩部分進(jìn)行关摇。
1.工程資源文件瘦身
工程資源文件主要是圖片,資源大小與圖片質(zhì)量和數(shù)量息息相關(guān)。
分析圖片的數(shù)量,質(zhì)量
使用腳本獲取工程中的所有圖片資源终吼,大小。工程共有722張圖片汉买,占用了8.2M的空間衔峰。圖片大小分類如圖:
其中10k以下占用率絕大部分,但還是有很多圖片超過了100k蛙粘,甚至有一些圖片達(dá)到了1M.太恐怖了垫卤。。出牧。建議把大文件替換穴肘。
圖片文件壓縮
無損壓縮工具ImageOptiom(推薦)。這是一款非常好的圖片壓縮工具舔痕,可以進(jìn)行無損壓縮评抚,能夠?qū)?png 和 jpeg 圖片文件進(jìn)行優(yōu)化,它能找到最佳的壓縮參數(shù)(在設(shè)置中可以設(shè)置壓縮比例伯复,80% 及以上是無損壓縮慨代,推薦使用),并通過消除不必要的信息(如文件的 EXIF 標(biāo)簽和顏色配置文件等)啸如,優(yōu)化后達(dá)到減小文件大小的效果侍匙。
使用了一張950k的圖片經(jīng)過ImageOptiom壓縮后586k,節(jié)省了38.3%的空間。
清除無用的資源文件
推薦使用工具LSUnusedResources叮雳。
接下來想暗,打開工具LSUnusedResources妇汗,點(diǎn)擊“Browse...”按鈕,選擇工程所在目錄说莫,點(diǎn)擊"Search"按鈕杨箭,即可開始搜索,如下圖所示:
工程中有234張圖片未引用储狭,但這個(gè)不準(zhǔn)因?yàn)樗皇轻槍υ创a互婿、Xib、Storyboard 和 plist 等文件晶密,先全文搜索其中可能是引用了資源的字符串擒悬,然后用資源名和字符串做匹配,而拼接的圖片會當(dāng)做未使用的圖片稻艰。因此搜索出后還需開發(fā)確認(rèn)是否是拼接圖片懂牧。
可執(zhí)行文件的瘦身
LinkMap文件是Xcode產(chǎn)生可執(zhí)行文件的同時(shí)生成的鏈接信息,用來描述可執(zhí)行文件的構(gòu)造成分尊勿,包括代碼段(__TEXT)和數(shù)據(jù)段(__DATA)的分布情況僧凤。
在Xcode中,選擇XCode -> Target -> Build Settings -> 搜map -> 把Write Link Map File選項(xiàng)設(shè)為YES元扔,并指定好linkMap的存儲位置躯保。
編譯后,到編譯目錄里找到該txt文件澎语,文件名和路徑就是上述的Path to Link Map File途事。這個(gè)LinkMap里展示了整個(gè)可執(zhí)行文件的全貌,列出了編譯后的每一個(gè).o目標(biāo)文件的信息(包括靜態(tài)鏈接庫.a里的)擅羞,以及每一個(gè)目標(biāo)文件的代碼段尸变,數(shù)據(jù)段存儲詳情。
import os
import re
import shutil
import sys
path = '/Users/mini5/Desktop/SmartHomeV6-LinkMap--.txt'
analyzeAllMoundle = False #統(tǒng)計(jì)每個(gè)模塊.o的統(tǒng)計(jì)大小
class SymbolModel:
file = ""
size = 0
def verify_linkmapfile(path):
if not os.path.isfile(path):
print("請輸入文件")
return False
file = open(path)
content = file.read()
file.close()
#查找是否存在# Object files:
if content.find("# Object files:") == -1:
print("輸入linkmap文件非法")
return False
#查找是否存在# Sections:
if content.find("# Sections:") == -1:
print("輸入linkmap文件非法")
return False
#查找是否存在# Symbols:
if content.find("# Symbols:") == -1:
print("輸入linkmap文件非法")
return False
return True
def symbolMapFromContent():
symbolMap = {}
reachFiles = False
reachSections = False
reachSymblos = False
file = open(path)
for line in file.readlines():
if line.startswith("#"):
if line.startswith("# Object files:"):
reachFiles = True
if line.startswith("# Sections:"):
reachSections = True
if line.startswith("# Symbols:"):
reachSymblos = True
else:
if reachFiles == True and reachSections == False and reachSymblos == False:
#查找 files 列表减俏,找到所有.o文件
location = line.find("]")
if location != -1:
key = line[:location+1]
if symbolMap.get(key) is not None:
continue
symbol = SymbolModel()
symbol.file = line[location + 1:]
symbolMap[key] = symbol
elif reachFiles == True and reachSections == True and reachSymblos == True:
#'\t'分割成三部分召烂,分別對應(yīng)的是Address,Size和 File Name
symbolsArray = line.split('\t')
if len(symbolsArray) == 3:
fileKeyAndName = symbolsArray[2]
#16進(jìn)制轉(zhuǎn)10進(jìn)制
size = int(symbolsArray[1],16)
location = fileKeyAndName.find(']')
if location != -1:
key = fileKeyAndName[:location + 1]
symbol = symbolMap.get(key)
if symbol is not None:
symbol.size = symbol.size + size
file.close()
return symbolMap
def sortSymbol(symbolList):
return sorted(symbolList, key=lambda s: s.size,reverse = True)
def buildResultWithSymbols(symbols):
results = ["文件大小\t文件名稱\r\n"]
totalSize = 0
for symbol in symbols:
results.append(calSymbol(symbol))
totalSize += symbol.size
results.append("總大小: %.2fM" % (totalSize/1024.0/1024.0))
return results
def buildCombinationResultWithSymbols(symbols):
#統(tǒng)計(jì)不同模塊大小
results = ["庫大小\t庫名稱\r\n"]
totalSize = 0
combinationMap = {}
for symbol in symbols:
names = symbol.file.split('/')
name = names[len(names) - 1].strip('\n')
location = name.find("(")
if name.endswith(")") and location != -1:
component = name[:location]
combinationSymbol = combinationMap.get(component)
if combinationSymbol is None:
combinationSymbol = SymbolModel()
combinationMap[component] = combinationSymbol
combinationSymbol.file = component
combinationSymbol.size = combinationSymbol.size + symbol.size
else:
#symbol可能來自app本身的目標(biāo)文件或者系統(tǒng)的動(dòng)態(tài)庫
combinationMap[symbol.file] = symbol
sortedSymbols = sortSymbol(combinationMap.values())
for symbol in sortedSymbols:
results.append(calSymbol(symbol))
totalSize += symbol.size
results.append("總大小: %.2fM" % (totalSize/1024.0/1024.0))
return results
def calSymbol(symbol):
size = ""
if symbol.size / 1024.0 / 1024.0 > 1:
size = "%.2fM" % (symbol.size / 1024.0 / 1024.0)
else:
size = "%.2fK" % (symbol.size / 1024.0)
names = symbol.file.split('/')
if len(names) > 0:
size = "%s\t%s" % (size,names[len(names) - 1])
return size
def analyzeLinkMap():
if verify_linkmapfile(path) == True:
print("**********正在開始解析*********")
symbolDic = symbolMapFromContent()
symbolList = sortSymbol(symbolDic.values())
if analyzeAllMoundle:
results = buildCombinationResultWithSymbols(symbolList)
else:
results = buildResultWithSymbols(symbolList)
for result in results:
print(result)
print("***********解析結(jié)束***********")
if __name__ == "__main__":
analyzeLinkMap()
這里可以統(tǒng)計(jì)每個(gè)文件或每個(gè)庫的大小娃承,該舍棄的舍棄奏夫。
其他方式
清理無用類,無用方法历筝,編譯選項(xiàng)優(yōu)化酗昼,好吧,我懶不想寫了梳猪。麻削。。。碟婆。。惕稻。竖共。。俺祠。公给。