<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          正經(jīng)分析iOS包大小優(yōu)化

          共 17527字,需瀏覽 36分鐘

           ·

          2021-09-30 14:03

          ????關(guān)注后回復(fù) “進(jìn)群” ,拉你進(jìn)程序員交流群????
          作者丨王徳亮
          來源丨搜狐技術(shù)產(chǎn)品(ID:sohu-tech)

          本文字?jǐn)?shù):8985

          預(yù)計閱讀時間:59分鐘

          背景

          包大小優(yōu)化是項目開發(fā)中不可避免會遇到的,網(wǎng)上關(guān)于包大小優(yōu)化的文章很多,每篇文章說的都不盡相同,筆者曾經(jīng)根據(jù)網(wǎng)上的文章做過包大小優(yōu)化,但效果不盡人意;因此筆者想根據(jù)已有的文章、知識結(jié)合自己的理解、實踐,做一份總結(jié)梳理,整理自己的包大小優(yōu)化邏輯,不光要知道怎么做可以讓包大小變化,還要知道為什么這么做能產(chǎn)生效果,所以就有了這篇文章。

          分析

          想要優(yōu)化安裝包大小,首先需要弄清楚影響安裝包大小的因素有哪些?之前筆者優(yōu)化包大小直接悶著頭就去瘦身,瘦來瘦去也沒瘦出個名堂,而且還跟別人說不清楚自己做了什么,為什么這么做?

          后來總結(jié)出來,做事之前要先思考、分析、最后再去做,要思考的是影響這件事的因素有哪些,一一列舉出來,查漏補缺;然后針對這些因素進(jìn)行分析,分析哪些因素是人為可控的,哪些因素是不能改變的,針對可控的部分要怎么優(yōu)化,不可控的部分是否能避免,最好可以使用思維導(dǎo)圖工具,一一記錄;然后按照思維導(dǎo)圖的整理出來的數(shù)據(jù),按步驟去執(zhí)行。

          回過頭來,針對安裝包大小,首先分析影響安裝包大小的因素,有:Xcode的設(shè)置、資源、代碼三個方面。那針對這幾個方面要怎么優(yōu)化?以及如何查看每一步優(yōu)化的結(jié)果?

          首先是怎么優(yōu)化的問題:

          Xcode的編譯設(shè)置優(yōu)化,Xcode設(shè)置影響的是生成包的大小,通過Xcode編譯選項優(yōu)化的設(shè)置,讓生成的ipa包變小,比如不含斷點調(diào)試、去掉異常支持等等。

          資源文件的優(yōu)化,資源不光有圖片資源,也包含代碼資源和其它導(dǎo)入的資源,可以通過分析安裝包構(gòu)成,看里面哪些部分比較大、不合理,從而進(jìn)行優(yōu)化。

          代碼的優(yōu)化,通過Link Map生成Link Map File,分析Link Map File各文件占用,結(jié)合Mach-O文件進(jìn)行分析優(yōu)化。

          然后是怎么查看每一步優(yōu)化的結(jié)果的問題:

          查看每一步的優(yōu)化結(jié)果,可以通過分析打包出來的ipa的大小,以及ipa的組成,與初始的ipa包大小比較,即可直觀得到優(yōu)化的結(jié)果。但可以更進(jìn)一步,分析ipa的構(gòu)成,對比優(yōu)化后的構(gòu)成,看每一步的操作具體影響的是包的哪一塊兒,從而導(dǎo)致包的大小發(fā)生了變化。所以先來看一下一個ipa的包包含哪些內(nèi)容,然后每一步優(yōu)化之后,對應(yīng)ipa的哪一部分發(fā)生了變化?

          安裝包的構(gòu)成

          iOS打包出來的ipa,本質(zhì)上是一個壓縮包,所以可以將.ipa的后綴改為.zip,然后進(jìn)行解壓縮,之后會得到一個Payload文件夾,里面又一個xxx.app的文件,這個xxx.app就是包含所有文件的包了,選中xxx.app,右鍵顯示包內(nèi)容,即可看到里面具體包含的東西了,大致如下:

          安裝包:

          • _CodeSignature:ipa包簽名文件
          • .lproj:     語言文件
          • Frameworks:     第三庫、SwiftSupport庫
          • Plugins:        App創(chuàng)建的擴(kuò)展,比如:Widget、Push、分享
          • Assets.car:     由Assets.xcassets生成的資源文件,里面包含各種分辨率的圖片
          • embedded.mobileprovision:證書配置文件
          • Info.plist:     項目配置
          • exec格式的xxx:   可執(zhí)行包
          • 其它資源文件
            • .mp3格式的文件
            • .html的文件
            • .json的文件
            • .png或者.jpg的文件

          示例如下,Ps:不得不說,筆者這個ipa包含的內(nèi)容真的是很全面了,各種的資源都有,哈哈哈

          筆者初始ipa包大小為22.9M,解壓縮之后.app的大小為57.1M,其中各部分大小明細(xì)如下

          內(nèi)容大小
          _CodeSignature93 KB
          .lproj4 KB
          Frameworks37.5 MB
          Plugins181 KB
          Assets.car4.9M
          embedded.mobileprovision8KB
          Info.plist6 KB
          exec格式的xxx13 MB
          其它資源文件1.4 MB

          了解了iPa包的組成之后,我們再回過頭來,按照Xcode的編譯優(yōu)化、資源文件優(yōu)化、代碼優(yōu)化的步驟來一步步分析。

          Xcode編譯設(shè)置

          一般這一步容易被人忽略,因為提到優(yōu)化最先能想到的就是資源優(yōu)化,比如圖片壓縮、無用代碼刪除等等,而對于Xcode自身的編譯優(yōu)化提及的反而不多。而且由于網(wǎng)上提供的參考針對每個項目可能結(jié)果都不一樣,有些編譯選項的設(shè)置是需要針與實際項目結(jié)合起來才可以,所以筆者這里整理和總結(jié)了一下:

          Xcode編譯優(yōu)化相關(guān):

          1. Build Settings中去掉異常支持,Enable C++ Exceptions和Enable Objective-C Exceptions設(shè)置為NO,Other C Flags添加-fno-exceptions;



            注意:Enable C++ Excptions和Enable Objective-C Exceptions是指項目支持對錯誤的異常處理,比如try catch、throw之類的;所以如果項目中使用的有類似的異常處理的,這個關(guān)閉了之后會報錯(Cannot use '@try' with Objective-C exceptions disabled)。包括宏定義中使用的有try{}、@finally{}之類的,比如@strongify等,如果關(guān)閉了最后打包的時候也會報錯。

            -fno-exceptions的意思是禁用異常機制,參考gcc,同樣,當(dāng)項目中有try thorw的時候,就不要設(shè)置這個選項為NO

            ?

            Before detailing the library support for -fno-exceptions, first a passing note on the things lost when this flag is used: it will break exceptions trying to pass through code compiled with -fno-exceptions whether or not that code has any try or catch constructs. If you might have some code that throws, you shouldn't use -fno-exceptions. If you have some code that uses try or catch, you shouldn't use -fno-exceptions.

          2. Build Settings -> Architectures,Release下設(shè)置為arm64


            Architectures指定工程被編譯成可支持哪些指令集類型,支持的指令集越多,就會編譯出多個指令集代碼的數(shù)據(jù)包,ipa包就會變大。默認(rèn)的standard architectures(armv7,arm64) 參數(shù),打的包里面有32位、64位兩份指令集。如果不需要32位的,可以在other中更改支持的指令集,從而使ipa包變小。

            ?

            armv6: iPhone, iPhone 3G, iPod 1G/2G
            armv7: iPhone 3GS, iPhone 4, iPhone 4S, iPod 3G/4G/5G, iPad, iPad 2, iPad 3, iPad Mini
            armv7s: iPhone 5, iPhone 5c, iPad 4
            arm64: iPhone X,iPhone 8(Plus),iPhone 7(Plus),iPhone 6(Plus),iPhone 6s(Plus), iPhone 5s, iPad Air(2), Retina iPad Mini(2,3)
            arm64e: XS/XS Max/XR/ iPhone 11, iPhone 11 pro x86_64: 模擬器64位處理器 i386: 模擬器32位處理器

            注意:Xcode 12之后,沒有了Valid Architectures選項的設(shè)置。另:如果把Architectures從standard architectures(armv7,arm64)改為arm64,則會發(fā)現(xiàn)target無法選擇模擬器運行了,所以建議Debug模式下修改為arm64、x86_64或者不修改,Release下設(shè)置為arm64。

            更新:經(jīng)朋友指點,這個地方Architectures的設(shè)置,還有另外一種設(shè)置方法,Architectures不修改,Excluded Architectures中設(shè)置Release模式下 Any iOS SDK -> armv7,也可以實現(xiàn)同樣的效果。設(shè)置了之后,就是Release下把armv7的指令集排除在外。選中target會發(fā)現(xiàn)默認(rèn)設(shè)置了 Any iOS Simulator SDK -> arm64,意思是模擬器的時候排除arm64指令集。

            如下:


          3. Build Settings -> Generate Debug Symbols設(shè)置為NO


            Generate Debug Symbols的意思是生成調(diào)試符號,當(dāng)這個選項設(shè)置為YES時,每個源文件在編譯成.o文件時,編譯參數(shù)多了-g和-gmodule,意思是generate complete debug info,所以產(chǎn)生的.o文件會大,從而最終生成的可執(zhí)行文件也就會變大。

            ?

            Generate Debug Symbols (GCC_GENERATE_DEBUGGING_SYMBOLS), Enables or disables generation of debug symbols. When debug symbols are enabled, the level of detail can be controlled by the Debug Information Format (DEBUG_INFORMATION_FORMAT) setting.

            注意Generate Debug Symbols設(shè)置為NO時,在Xcode中設(shè)置的斷點不會中斷,即不能斷點調(diào)試。且最后不能生成DSYM文件,即使Debug Information Format設(shè)置了,也不能生成,因為首先要有調(diào)試信息然后才能生成DSYM文件,而設(shè)置為NO,意味著不產(chǎn)生調(diào)試信息,所以也就沒辦法生成DSYM文件。所以建議不要設(shè)置。

          4. Build Settings -> Deployment Postprocessing,Debug模式下設(shè)置NO,Release下設(shè)為YES

            Deployment Postprocessing是Strip配置的總開關(guān)


            官方解釋:

            ?

            Deployment Postprocessing (DEPLOYMENT_POSTPROCESSING), If enabled, indicates that binaries should be stripped and file mode, owner, and group information should be set to standard values.

            注意:Deployment Postprocessing是Strip配置的總開關(guān),只有這個設(shè)置為YES之后,下面的Strip Linked Product、Strip Debug Symbols During Copy的設(shè)置才會生效。

            1. Build Settings -> Strip Linked Product,Debug下設(shè)置為NO,Release下設(shè)置為YES

              對最后生成的二進(jìn)制文件進(jìn)行strip,去除不必要的符號信息,Release下可以為YES。

              官方解釋:

              ?

              Strip Linked Product (STRIP_INSTALLED_PRODUCT), If enabled, the linked product of the build will be stripped of symbols when performing deployment postprocessing.

              注意:如果Deployment Postprocessing不打開,該選項沒有作用。去除了符號信息之后需要使用dSYM來進(jìn)行符號化,所以需要將 Debug Information Format 修改為DWARF with dSYM file(Release下),如果在Debug下設(shè)置為DWARF with dSYM file那么在崩潰時將無法看到堆棧信息。

            2. Build Settings -> Strip Debug Symbols During Copy,Debug下設(shè)置為NO,Release下設(shè)置為YES

              文件拷貝編譯階段是否進(jìn)行strip,設(shè)置為YES之后,會把拷貝進(jìn)項目包的三方庫、資源或者Extension的Debug Symbol去除。

              官方解釋:

              ?

              Strip Debug Symbols During Copy (COPY_PHASE_STRIP), Specifies whether binary files that are copied during the build, such as in a Copy Bundle Resources or Copy Files build phase, should be stripped of debugging symbols. It does not cause the linked product of a target to be stripped—use Strip Linked Product (STRIP_INSTALLED_PRODUCT) for that.

              注意:如果Deployment Postprocessing不打開,該選項沒有作用

            3. Build Settings -> Symbols Hidden by Default,Debug模式下設(shè)置為NO,Release下設(shè)置為YES


              Symbols Hidden by Default會把所有符號都定義成"private extern",移除符號信息

              官方解釋:

              ?

              Symbols Hidden by Default (GCC_SYMBOLS_PRIVATE_EXTERN), When enabled, all symbols are declared private extern unless explicitly marked to be exported using attribute((visibility("default"))) in code. If not enabled, all symbols are exported unless explicitly marked as private extern. See Controlling Symbol Visibility in C++ Runtime Environment Programming Guide.

          5. Build Settings -> Make Strings Read-Only設(shè)置為YES


            復(fù)用字符串字面量

            官方解釋:

            ?

            Make Strings Read-Only (GCC_REUSE_STRINGS), Reuse string literals.

          6. Build Settings -> Dead Code Stripping設(shè)置為YES


            消除無效代碼,C/C++/Swift 等靜態(tài)語言編譯器會在 link 的時候移除未使用的代碼,對于OC等動態(tài)語言是無效的

            官方解釋:

            ?

            Dead Code Stripping (DEAD_CODE_STRIPPING), Activating this setting causes the -dead_strip flag to be passed to ld(1) via cc(1) to turn on dead code stripping.

          7. Pod優(yōu)化,如果項目是OC的,但CocoaPod中有使用了Swift庫,打開了use_frameworks!,可以優(yōu)化為針對單個Swift庫使用use_frameworks!而不是全部第三方庫都使用。代碼如下:

            # Podfile
            # 下面的這行代碼還是打開,不需要注釋掉
              use_frameworks!

            # 之前的其它代碼


            # 然后添加下面的代碼,xxx是要打包成framework的庫
            dynamic_frameworks = ['xxx']
            pre_install do |installer|
                installer.pod_targets.each do |pod|
                    if !dynamic_frameworks.include?(pod.name)
                        def pod.static_framework?;
                         true
                        end
                        def pod.build_type;
                            Pod::BuildType.static_library
                        end
                    end
                end
            end

            在OC的項目中,Podfile如果引用了Swift的第三方庫,一般都會直接打開use_frameworks!,對應(yīng)的Pod中所有的庫都會打包成動態(tài)庫,以及Swift和OC庫的依賴問題會導(dǎo)致依賴庫增加,會造成包體積增大。

            修改方法有兩種:

            這兩種修改方法都可以顯著減小包的體積。通過替代庫可避免導(dǎo)入Swift相關(guān)的依賴基礎(chǔ)庫,且對應(yīng)的use_frameworks!可以注釋掉,相對來說,包會更小,但是改動比較大,需要把之前的庫替換掉。而通過hook的方式,針對個別的庫打包成動態(tài)庫,其余的打包成靜態(tài)庫,項目修改比較小,包體積也能減小。具體采用哪種視具體項目情況而定。

            注意:Podfile按照上面的修改了之后,需要重新使用Pod install命令更新,然后編譯時可能會有報錯。

            • 去除依賴的Swift第三方庫,找對應(yīng)的OC庫替代;
            • 通過Podfile里hook的方式,改動態(tài)庫為靜態(tài)庫
            1. 如果報的錯誤是,'RuntimeError - [Xcodeproj] Consistency issue: build setting ARCHS has multiple values: {"Debug"=>"$(ARCHS_STANDARD)", "Release"=>["arm64"]}',可以先把Architecture中的值改為一樣的,Pod install之后再改為不同指令集的。
            2. 因為之前第三方庫打包成framework的問題,如果遇到xxx framework not found的錯誤,在Build Setting中Other Linker Flags中進(jìn)行修改,把對應(yīng)的已經(jīng)不是framework的庫從其中移除即可。示例如下:
          8. Asset Catalog Compiler編譯設(shè)置優(yōu)化,Build Settings -> Asset Catalog Compiler - Options 中Optimization改為space


            這個選項可以改變actool在構(gòu)建Assets.car時選取的編碼壓縮算法,減少包大小??梢允褂孟旅娴拿顧z查Assets.car中圖片的編碼壓縮算法。

            // 可以把對應(yīng)信息生成.json文件,用于對比不同
             xcrun --sdk iphoneos assetutil --info Assets.car > Assets.json

            比如筆者的設(shè)置了之后,對比查看信息如下,可發(fā)現(xiàn)壓縮的算法不同,占用空間的大小也不同

          9. Build Settins -> Optimization Level改為-Oz


            Optimization Level默認(rèn)為-Os,-Oz是Xcode 11之后才出現(xiàn)的編譯優(yōu)化選項,核心原理是對重復(fù)的連續(xù)機器指令外聯(lián)成函數(shù)進(jìn)行復(fù)用,因此開啟Oz,能減少二進(jìn)制的大小,但同時會帶來執(zhí)行效率但額外消耗。可參考What's New in Clang and LLVM

            在What's New in Clang and LLVM的Presentation Slides中,蘋果給出了Optimization Level各參數(shù)優(yōu)化的選擇對比,如下圖,對于性能要求高的,建議選擇-O2和-O3,對于包大小敏感的,可選擇-Os和-Oz,默認(rèn)-Os是性能和大小平衡比較好的。最終選擇什么,需要讀者根據(jù)自己實際項目而定。Optimization Level各參數(shù)對比:

            官方解釋:

            ?

            Smallest, Aggressive Size Optimizations: This setting enables additional size savings by isolating repetitive code patterns into a compiler generated function. -Oz

          Xcode編譯設(shè)置優(yōu)化總結(jié)如下:

          優(yōu)化結(jié)果

          筆者最初項目大小最初為22.9M,設(shè)置了Pod優(yōu)化之后大小縮減為21M,設(shè)置了Asset Catalog Compiler - Options 中Optimization為space之后,項目大小變?yōu)?0.7M,再設(shè)置了上面其它Xcode編譯優(yōu)化之后,項目變?yōu)?3.2M(筆者把Architecture設(shè)為arm64)

          設(shè)置Pod優(yōu)化之后各部分對比

          內(nèi)容大小
          _CodeSignature67 KB
          .lproj4 KB
          Frameworks21.4 MB
          Plugins181 KB
          Assets.car4.9M
          embedded.mobileprovision8KB
          Info.plist6 KB
          exec格式的xxx19.2 MB
          其它資源文件1.4 MB

          簡單對比大小之后可以發(fā)現(xiàn),F(xiàn)rameworks從35M減少到了21.4M,而exec文件從13M升到了19.2M,但總的ipa包大小變?yōu)榱?1M,比之前但22.9M降低了。

          為什么會這樣呢?還記得Frameworks文件夾里放的是什么內(nèi)容嗎?Framework中放的Pod中設(shè)置的第三方的動態(tài)庫、以及Swift Support庫。仔細(xì)觀察Frameworks文件夾中的內(nèi)容,可以發(fā)現(xiàn),之前在這里面的第三方的.framework,除了指定的打包成動態(tài)庫的第三方xxx、和Swift Support庫還在,其他的都不見了。

          還記得筆者改的是什么嗎?筆者把Pod中第三方庫從都使用動態(tài)庫改為了個別使用動態(tài)庫、其它使用靜態(tài)庫。因為動態(tài)庫和靜態(tài)庫鏈接的方式的不同,動態(tài)庫鏈接時不復(fù)制,在程序啟動后用動態(tài)加載,所以是單獨放在Framework文件夾下;而靜態(tài)庫是鏈接時會被完整的復(fù)制到可執(zhí)行文件中。所以,于是就出現(xiàn)了這樣的結(jié)果,F(xiàn)rameworks文件大小減少,而可執(zhí)行文件大小增加。

          設(shè)置了Asset Catalog Compiler - Options 中Optimization為space后對比

          內(nèi)容大小
          _CodeSignature67 KB
          .lproj4 KB
          Frameworks21.4 MB
          Plugins181 KB
          Assets.car4.7M
          embedded.mobileprovision8KB
          Info.plist6 KB
          exec格式的xxx19.2 MB
          其它資源文件1.4 MB

          對比之后發(fā)現(xiàn),Assets.car中大小從4.9M減少到了4.7M,總的打包出來的ipa大小減少了0.3M,說明asset壓縮有效。

          設(shè)置了Optimization Level為-Oz后對比

          內(nèi)容大小
          _CodeSignature67 KB
          .lproj4 KB
          Frameworks21.4 MB
          Plugins181 KB
          Assets.car4.7M
          embedded.mobileprovision8KB
          Info.plist6 KB
          exec格式的xxx18.9 MB
          其它資源文件1.4 MB

          對比之后發(fā)現(xiàn),exec的可執(zhí)行文件大小從19.2M減少到了18.9M,說明優(yōu)化有效,但總的打包出來的ipa大小并沒有改變(上傳到fir后,包小了0.03M),故而最終這個選項筆者最終沒有打開。

          Xcode其它編譯設(shè)置優(yōu)化之后對比

          內(nèi)容大小
          _CodeSignature67 KB
          .lproj4 KB
          Frameworks11.1 MB
          Plugins83 KB
          Assets.car4.7M
          embedded.mobileprovision8KB
          Info.plist6 KB
          exec格式的xxx10.3 MB
          其它資源文件1.4 MB

          簡單對比大小之后可以發(fā)現(xiàn),F(xiàn)rameworks從21.4M減少到了11.1M,Plugins從181KB減少到了83KB,exec文件也從19.2M減少到了10.3M。

          同樣為什么會這樣呢?筆者設(shè)置了strip移除了符號信息,設(shè)置了打包只生成arm64架構(gòu)指令集的包,所以Framework和exec都變小了,那Plugin呢?Plugin中是App創(chuàng)建的擴(kuò)展,筆者針對通知擴(kuò)展,也設(shè)置了只生成arm64指令集的包,所以Plugin也變小了。

          優(yōu)化總結(jié)


          也許有人覺得筆者目前項目包沒有太大,做這個優(yōu)化是否有意義?在筆者看來,包大小優(yōu)化應(yīng)該是習(xí)慣,不是因為包大了才去優(yōu)化,而是因為覺得有優(yōu)化空間,所以才優(yōu)化。打包出來ipa的包小,說明歷史負(fù)擔(dān)不重,船小好調(diào)頭,編譯打包速度也快,而且試錯成本也低,恰恰是正應(yīng)該優(yōu)化的時候。優(yōu)化總結(jié)出來的經(jīng)驗教訓(xùn),落入文檔,后續(xù)開發(fā)時能時刻注意,對開發(fā)來說是更好的。

          資源文件優(yōu)化

          資源文件的優(yōu)化,通常來說是比較簡單的,但是資源文件的優(yōu)化是需要持續(xù)進(jìn)行的,前面介紹的Xcode編譯設(shè)置優(yōu)化,配置好了之后,后續(xù)開發(fā)過程中只要不修改配置,都無需重復(fù)關(guān)注。但資源文件不同,隨著項目的迭代,會不斷引入新的資源文件,不斷有廢棄資源的產(chǎn)生,所以資源文件的優(yōu)化是要持續(xù)進(jìn)行的。

          資源文件的優(yōu)化分為兩部分,即:無用資源的刪除、已用資源的壓縮。在這里建議分先后順序,即先做刪除再做壓縮,因為如果先壓縮了,結(jié)果發(fā)現(xiàn)是無用資源,就白白浪費了力氣。

          無用資源的刪除:

          • 已定義未使用的代碼文件
          • 已廢棄業(yè)務(wù),代碼還在
          • 已引用的圖片但未使用
          • 某些重復(fù)資源導(dǎo)入

          已用資源的壓縮:

          • 項目中引入圖片、網(wǎng)頁、json、音頻等文件的壓縮

          下面一步步的來實踐:

          無用資源的刪除

          隨著項目的迭代,每個項目都會或多或少存在冗余??赡苁情_發(fā)了的功能未上線但產(chǎn)品讓保留,保留著保留著就忘記了;可能是已下線的業(yè)務(wù),沒人通知到開發(fā),于是代碼邏輯一直都在;可能是刪除某些業(yè)務(wù)代碼時,對應(yīng)的圖片資源未刪除;又或者是每個開發(fā),導(dǎo)入各自熟悉的第三方庫使用。

          這部分的優(yōu)化在筆者看來,也分為兩步,一是預(yù)防,一是治理。筆者個人感覺預(yù)防比治理更重要。因為預(yù)防是前置,預(yù)防到位了,治理就會很輕松。

          預(yù)防

          上面說的場景,讀者可能都遇到過,那要怎么做才能避免出現(xiàn)上面的情況,或者盡量少出現(xiàn)上面的情況?

          筆者個人想法是通過規(guī)范的流程來,有一套規(guī)范的流程,按流程執(zhí)行,避免出現(xiàn)信息不對等的情況。

          產(chǎn)品和開發(fā)之間的信息不對等,會導(dǎo)致業(yè)務(wù)相關(guān)的冗余,產(chǎn)品知道具體業(yè)務(wù)的數(shù)據(jù),而開發(fā)不清楚,所以可以通過定期同步給開發(fā),讓開發(fā)也能了解到對應(yīng)業(yè)務(wù)是否活躍,從而及時對項目進(jìn)行優(yōu)化。

          開發(fā)之間的信息不對等,會導(dǎo)致各自開發(fā)自己的,重復(fù)造輪子,所以可以通過建立公共文檔,開發(fā)的流程規(guī)范、項目使用第三方庫的規(guī)范、設(shè)計規(guī)范、代碼規(guī)范都列舉出來,每個人都能根據(jù)對應(yīng)的文檔了解到對應(yīng)項目的信息,每個人開發(fā)都應(yīng)該有一套統(tǒng)一的標(biāo)準(zhǔn)。這樣就可以避免一人一套代碼的問題。

          具體的規(guī)范流程讀者可以針對自己公司的實際情況來,可以思考一下,之前開發(fā)中是否出現(xiàn)了類似的問題,出現(xiàn)了之后是否有改變,怎么能避免再次出現(xiàn)同樣的問題?

          治理

          針對無用資源的刪除

          • 已定義未使用的代碼

            可使用AppCode進(jìn)行分析,打開AppCode,待索引完成后,選擇頂部菜單中的Code->Inspect Code,然后選擇范圍,whole Project點擊OK,等待AppCode靜態(tài)分析即可。靜態(tài)分析完以后,可以在Unused code里看到所有的無用代碼。 AppCode中無用代碼靜態(tài)分析的類型有以下幾種:

          • 內(nèi)容大小
            Unused class無用類
            Unused import statement無用類引入聲明
            Unused property無用屬性
            Unused method無用方法
            Unused parameter無用參數(shù)
            Unused instance variable無用實例變量
            Unused local variable無用局部變量
            Unused value無用值聲明
            Unused macro無用宏定義
            Unused global declaration無用全局證明

            AppCode靜態(tài)分析結(jié)果出來之后,刪除前要經(jīng)過確認(rèn),因為靜態(tài)分析的結(jié)果可能會有誤差,比如針對performSelector調(diào)用的方法就會被檢測為沒有調(diào)用。

          • 已廢棄業(yè)務(wù),代碼還在

            需要梳理業(yè)務(wù)流程,結(jié)合線上業(yè)務(wù)數(shù)據(jù)點擊量,同產(chǎn)品和業(yè)務(wù)確認(rèn)對應(yīng)功能是否下線,從而決定是否移除對應(yīng)的業(yè)務(wù)模塊代碼。

          • 已引入未使用圖片

            推薦使用工具LSUnusedResources,原理大致是遍歷資源目錄下后綴 ["imageset", "jpg", "png"...] 的文件,然后在源文件 ["m", "swift", "xib", "storyboard"...] 中字符串匹配,無匹配則是無用的資源文件。

            使用時注意勾選Ignore similar name,然后點擊右上角的Browse選中要掃描的項目地址,點擊右下角的search,就會開始掃描,結(jié)果會在底部Unused Results中展示出來,然后CMD+A全選,export,導(dǎo)出到一個文本文件中。也可以在對應(yīng)單條Item上面雙擊,會打開對應(yīng)的文件夾。建議刪除前在項目中搜索確認(rèn),是否確實沒有使用(類似字符串中間替換的可能會被掃描出來,所以刪除前需要確認(rèn))

          • 某些重復(fù)資源的導(dǎo)入

            重復(fù)資源的導(dǎo)入,分為兩個方面,一方面是針對第三方SDK,另一方面是項目文件。

            • 針對第三方SDK

              項目中功能類似的SDK建議保留一個,比如埋點統(tǒng)計的友盟、TalkingData等,線上日志分析的聽云、Bugly等,又或者網(wǎng)絡(luò)請求、UI布局的類庫,建議分析相同功能的類庫,結(jié)合實際情況,保留一個即可;另外,有些第三方類庫導(dǎo)入時,可只導(dǎo)入實際使用的部分,不需全量導(dǎo)入,也是可以優(yōu)化的地方。

            • 針對項目文件

              使用 fdupes 工具進(jìn)行重復(fù)文件掃描,原理是:通過校驗所有資源的 MD5,篩選出項目中的重復(fù)資源,文件比較順序是大小對比 > 部分 MD5 簽名對比 > 完整 MD5 簽名對比 > 逐字節(jié)對比。來自:包體積大?。菏萆?/p>

              fdupes使用如下:


              // 1. 首先安裝fdupes
              brew install fdupes

              // 2. 使用,其中xxx是要掃描的目錄,yyy.txt是掃描結(jié)果輸出的文件
              fdupes -Sr /Users/.../xxx/ > /Users/.../yyy.txt 


              輸出結(jié)果類似于下面這樣,通常相同文件可通過修改引用,僅保留一份源文件就可以。

              [email protected]

          已用資源的壓縮

          • 項目中引入圖片、網(wǎng)頁、json、音頻等文件的壓縮

            網(wǎng)頁的壓縮指的是,放入APP資源中的js文件,最好是經(jīng)過H5端壓縮后的。

            json文件的壓縮,如果不是打開APP時馬上要用到的數(shù)據(jù),可采取把對應(yīng)資源放到服務(wù)端,下載后使用。

            音頻文件的壓縮,則是在可接受的范圍之內(nèi),選擇系統(tǒng)可支持的壓縮比率高的格式。

            而最需要注意的是圖片的壓縮,圖片的壓縮,分為幾個部分

            • Compress PNG Files
              打包的時候自動對圖片進(jìn)行無損壓縮

            • Remove Text Medadata From PNG Files
              移除 PNG 資源的文本字符

            • resource_bundles:允許定義當(dāng)前 Pod 庫的資源包的名稱和文件。用 hash 的形式來聲明,key 是 bundle 的名稱,value 是需要包括的文件的通配 patterns。CocoaPods 官方強烈推薦使用 resource_bundles,因為用 key-value 可以避免相同名稱資源的名稱沖突。同時建議 bundle 的名稱至少應(yīng)該包括 Pod 庫的名稱,可以盡量減少同名沖突。使用resource_bundles會為指定的資源打一個.bundle,所以獲取圖片時要注意指定bundle的位置:

                  
              NSString *bundlePath = [[NSBundle bundleForClass:[self class]].resourcePath stringByAppendingPathComponent:@"/PAX.bundle"];
              NSBundle *resource_bundle = [NSBundle bundleWithPath:bundlePath];
              UIImage *image = [UIImage imageNamed:@"xxxx" inBundle:resource_bundle compatibleWithTraitCollection:nil];

            • resources:使用 resources 來指定資源,被指定的資源只會簡單的被 copy 到目標(biāo)工程中(主工程)。官方認(rèn)為用 resources 是無法避免同名資源文件的沖突的,同時,Xcode 也不會對這些資源做優(yōu)化。

            1. 單色圖標(biāo)、簡單的功能圖標(biāo),建議使用IconFont,矢量圖標(biāo)庫的格式,既能統(tǒng)一規(guī)范格式,又能減少資源文件大小。圓角和陰影圖片盡量代碼實現(xiàn)。

              IconFont的使用可參考筆者之前寫的Demo——IconFont

            2. 針對普通圖片,可以調(diào)用tinyPNG API進(jìn)行壓縮,這里可以使用筆者之前修改的腳本BatchProcessImage,調(diào)用的是tinyPNG的API可以一次性壓縮500張圖片,而且只需要指定項目目錄,會自動壓縮后替換原來的圖片,不需要手動導(dǎo)入導(dǎo)出圖片。使用可參考鏈接BatchProcessImage,需要注意的是注意python版本,python3和python,以及pip3和pip的選擇,安裝依賴庫的時候使用的哪個python版本,最后調(diào)用的腳本命令的時候就要用對應(yīng)的python版本。

              另:如果項目圖片超出500張,可以修改一下腳本文件,即:壓縮過程中把壓縮處理過的圖片存儲下來,然后第二次執(zhí)行時,對于壓縮過的不處理,就可以接著上次壓縮到的繼續(xù)壓縮了。而如果需要對壓縮過的再次壓縮,只需要把存儲下來的壓縮過的圖片名字清除即可。

            3. 放入xcassets里的2x和3x圖片,在上傳時,會根據(jù)具體設(shè)備分開對應(yīng)分辨率的圖片,不會同時包含。而放入Bundle中的都會包含。所以要盡量把圖片放入xcassets中。但是,根據(jù)抖音品質(zhì)建設(shè) - iOS 安裝包大小優(yōu)化實踐篇中介紹的,Assets.car編譯過程中有時會選擇一些圖片,拼湊成一張大圖來提高圖片的加載效率。被放進(jìn)這張大圖的小圖會變?yōu)橥ㄟ^偏移量的引用,建議使用頻率高且小的圖片放到Asset.car中,Asset.car能保證加載和渲染速度最優(yōu)。但是大的圖片(大于100K)就不要放入Asset.car中了。大的圖片可以考慮將圖片轉(zhuǎn)成WebP。WebP是Google公司的一個開源項目,能夠把圖片壓縮到很小,但是肉眼看不出來差別,目前iOS常用的圖片顯示類庫都用支持該格式解析的拓展??墒褂胕Sparta進(jìn)行批量轉(zhuǎn)換。

            4. 私有Pod庫中的資源文件,建議在Pod庫里面的Resource目錄下新建Asset Catalog文件,命名為Images.xcassets,私有庫使用的圖片放入這里,然后手動修改該SDK的podspec,指定resource_bundles使用Images.xcassets。


                  s.resource_bundles = {
                      'xxsdk' => ['xxx/Assets/.../*.xcassets']
                  }

              Ps: Pod資源文件引用方式有 resource_bundles 和 resources,推薦使用 resource_bundles。參考深入探索 iOS 包體積優(yōu)化

            5. 最后,是Xcode中關(guān)于圖片壓縮的設(shè)置,有時候壓縮了圖片之后,發(fā)現(xiàn)包大小并沒有改變太多,可能是因為Xcode的Compress PNG Files選項的原因。建議如果自己對圖片進(jìn)行了壓縮,就可以把Xcode的Compress PNG Files設(shè)置為NO。

          資源文件優(yōu)化總結(jié)如下:

          優(yōu)化結(jié)果

          筆者項目的ipa大小從Xcode編譯優(yōu)化后的大小為13.2M,經(jīng)過了資源文件優(yōu)化之后大小縮減為10.3M。

          資源文件優(yōu)化之后各部分對比

          內(nèi)容大小
          _CodeSignature67 KB
          .lproj4 KB
          Frameworks11.1 MB
          Plugins83 KB
          Assets.car2.4M
          embedded.mobileprovision8KB
          Info.plist6 KB
          exec格式的xxx10.3 MB
          其它資源文件952 KB

          簡單對比之后發(fā)現(xiàn),Assets.car從4.7M減少到2.4M,其它資源文件從1.4M減少到952KB,說明資源壓縮有效。

          優(yōu)化總結(jié)

          見圖,??


          監(jiān)控機制?

          瘦身完成之后,如何保證包大小不會再次迅速增大?就像減肥之后不會迅速反彈一樣?就需要依賴適當(dāng)?shù)谋O(jiān)控機制和合理的流程規(guī)范來控制。

          監(jiān)控機制保證實時發(fā)現(xiàn)問題,每次打包完成后,運行腳本比較包大小差異,如果有增大超過了設(shè)置的閾值,則郵件通知相關(guān)開發(fā),開發(fā)關(guān)注排查是什么原因?qū)е掳笮∽兇?;同時做好記錄,每次打包的包大小變化及時注意記錄,造成包大小變化的原因,落入文檔。

          流程規(guī)范是用于保證每個項目開發(fā)者知曉開發(fā)中注意什么,養(yǎng)成好的開發(fā)習(xí)慣,避免造成包大小的突然變大。

          • 引入新的三方庫時,要考慮是否已有同類型的庫,是否可以自己實現(xiàn),是否會造成體積增大。盡量避免Objective-C和Swift混編,優(yōu)先引用相同語言類型的庫
          • 新增的圖片資源,關(guān)注大小,考慮是否能用Iconfont,是否能代碼實現(xiàn),注意放入項目的位置,如果體積太大,壓縮后使用
          • 廢棄模塊不要保留,及時清理
          • 及時關(guān)注包大小變化,對于包大小變化的原因及結(jié)果整理落入文檔,反饋總結(jié)

          總結(jié)

          截止目前為止,筆者總體項目優(yōu)化結(jié)果如下:

          優(yōu)化內(nèi)容ipa大小
          原始22.9M
          Xcode編譯優(yōu)化-Pod優(yōu)化后21M
          Xcode編譯優(yōu)化-Asset Catalog Compiler編譯設(shè)置優(yōu)化后20.7M
          Xcode編譯優(yōu)化-其它13.2M
          資源優(yōu)化10.3M

          達(dá)到了預(yù)期瘦身的效果,雖然還有更進(jìn)一步優(yōu)化的空間,比如把項目中唯一引用的Swift的第三方庫改為OC的,從而可以去除混編,能大幅縮減項目大小,但是由于需要改動業(yè)務(wù)代碼,筆者暫時擱置了。

          總的來說,筆者在業(yè)務(wù)代碼沒有改動的情況下,經(jīng)過Xcode編譯優(yōu)化和資源文件壓縮,把包大小從22.9M壓縮到了10.3M,就結(jié)果來說是超出了預(yù)期。

          但是作為優(yōu)化包大小的實踐來說還有待未完待續(xù)的地方,就是最后一步的代碼優(yōu)化,筆者打算單獨抽一篇文章,來補充一下,使用代碼優(yōu)化的流程邏輯。

          引用

          1. 包體積大小:瘦身
          2. iOS包體積優(yōu)化
          3. 抖音品質(zhì)建設(shè) - iOS 安裝包大小優(yōu)化實踐篇
          4. 干貨!京東商城iOS App瘦身實踐
          5. iOS 優(yōu)化IPA包體積(今日頭條)
          6. 深入探索 iOS 包體積優(yōu)化
          7. 干貨|今日頭條iOS端安裝包大小優(yōu)化—思路與實踐
          8. 今日頭條 iOS 安裝包大小優(yōu)化—— 新階段、新實踐
          9. iOS 優(yōu)化ipa包,減小安裝包大小
          10. iOS微信安裝包瘦身

          -End-

          最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來,可以說是程序員面試必備!所有資料都整理到網(wǎng)盤了,歡迎下載!

          點擊??卡片,關(guān)注后回復(fù)【面試題】即可獲取

          在看點這里好文分享給更多人↓↓

          瀏覽 19
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久9视频 | 国产视频一区在线 | 色情网站在线 | 日韩黄色直播在线视频网站 | 国产三级电影在线 |