iOS-效能最佳化的那些事

qianby發表於2021-09-09

前言
本人在這家公司已經三年多了,這款三年多我一直在做的APP也爛熟於心,APP也0到1到目前的500萬的使用者量;對於APP的功能來說也是比較全面的,用到的技術知識點也比較多吧,APP的最佳化也是一直在做的事情,而且APP效能的最佳化也不是一朝一夕的事情,在此離別之際,我將詳細說明講解一下我在三年裡對APP效能最佳化方面做過的一些事,大家仁者見仁智者見智,也歡迎大家進群提供寶貴的意見和建議!

基礎最佳化
使用ARC,現在的iOS開發大家用的都是ARC,幾乎沒有人再去使用MRC了,使用ARC的好處就是不用再時時刻刻注意要釋放建立的物件了;避免使用xib或者storyboard。

這裡說一下xib和sb的缺點吧,如下:

1.佔用API包比較大;
2.導致APP啟動時間比較耗時,因為在APP啟動main()以前需要載入他們;
3.載入速度比較慢;
4.後期的版本更新迭代維護時間成本比較高;
5.多人開發容易引起衝突。

列表圖片最佳化
列表不論在哪一個APP中是使用最為廣泛的一款控制元件了,在我的APP中也不例外,我們的APP只要功能列表類似於微信的朋友圈,圖片有0~9張的形式還可以是影片檔案;
先說一說圖片吧,如果一個列表都是9張圖片,列表在載入和滑動的時候會耗用過多的記憶體;我在這裡是把圖片這一塊單獨抽出來做一個圖片資源的封裝,然後根據建立的圖片容器(UIImageView)的大小載入縮圖,我們公司的APP儲存用的是七牛雲端儲存,所以在獲取圖片資源的時候,只需要設定對應的欄位就可以拿到縮圖了,點選的檢視大圖的時候才檢視原圖;

再說說影片,影片的處理邏輯和圖片差不多,這裡在CELL上我們使用一個UIImageView來替代影片播放器,可以去出影片的第一幀作為封面,點選圖片的時候才呼叫我們封裝好的影片播放元件。

複用機制
說道複用,我們可能常用的有CELL的複用,其實我們也可以自己寫我們的服用,就比如上圖中的列表,我們一般採用的思路就是在主控制器上新增一個UIScrollView,再根據有多少個小標題型別建立多少個子控制器,接著把子控制器新增到主控制器的childViewControllers中,最後把子控制器的檢視新增到UIScrollView上,有沒有更節省記憶體空間的做法呢???
當然有的,我們可以只建立5個控制器然後丟到我們的可重用陣列中,根據每次滑動去載入不同的快取資料;
還比如我在直播間建立刷禮物的檢視動畫的時候,因為最多隻能顯示三條,那麼我只會建立三個檢視,當有大於三條的禮物資訊過來的時候也只會建立三個檢視,然後丟進我的可重用陣列裡面,每個檢視動畫完成以後才重新複製Model,如下圖左下角的禮物動畫最多三個,最少沒有:

就如上面所說,在專案中我們可以在很多地方建立屬於我們自己的重用機制!

離屏渲染
在開發中,我們常用有圓角處理、陰影、遮罩等等;先說說圓角最佳化吧,我們一般設定圓角的方式如下:

view.layer.cornerRadius = 10;
view.layer.masksToBounds = YES;
這樣處理的渲染機制是GPU在當前螢幕緩衝區外新開闢一個渲染緩衝區進行工作,也就是離屏渲染,這會給我們帶來額外的效能損耗,如果這樣的圓角操作達到一定數量,會觸發緩衝區的頻繁合併和上下文的的頻繁切換,效能的代價會宏觀地表現在使用者體驗上——掉幀。

最佳化方案:
a.可以做一個透明的png圖片蓋在上面;
b.使用貝塞爾曲線UIBezierPath和Core Graphics框架畫出一個圓角;
c.使用CAShapeLayer和UIBezierPath設定圓角;
d.也可以將圖片的處理放在服務端(比如:七牛雲儲存就可以設定圖片的圓角);
e.儘量把view設定成不透明的。

說明:離屏渲染還可以做很多東西,具體的可以自行查詢!

懶載入
這裡的懶載入主要說的是懶載入思想,可以懶載入的型別有很多,當然好處有很多,最主要的是可以節省記憶體資源;

重大開銷物件
我們在APP使用過程中會用到很多重大開銷物件(比如NSDateFormatter和 NSCalendar),如我們在列表需要計算使用者年齡的時候會經常用到NSDateFormatter,還有一些時間的格式化輸出,或者在網路請求的時候需要傳一些時間戳,所以我們可以把NSDateFormatter放在單利裡面,這樣就不用經常建立了,其他的也是同樣的道理。

還有就是,像年齡生日什麼的最好不要由前端APP來計算,最好放在伺服器上,由服務端計算好然後再傳給我們。

記憶體警告
如果程式在執行過程中發生了記憶體警告(didReceiveMemoryWarning),我們需要快速應急處理一下,不要用不了多久APP就會被殺死掉,所在在收到記憶體警告主要思路就是想著去清理棧上的東西,以後我們可以做以下幾點:

a.清理不需要的已經建立的物件,不論是試圖還是工具類;

b.釋放單利裡面不需要的物件,比如上文提高過得重大開銷物件;

c.如果有正在下載的任務,取消或者暫停全部任務;

d.如果使用了SDWebImage,可以清理一下快取clearMemory;

主執行緒流暢
不要阻塞主執行緒,要保證主執行緒的流程性;我們不要把一些重大開銷物件放到主執行緒裡面,我們可以建立子執行緒去處理這些事情;比如多個網路請求結束一起重新整理UI、多個動畫效果、資料的讀寫操作等等;

資料快取
做快取這個事情,一方面減小記憶體的小號,還有很重要的一方面就是最佳化使用者體驗;可以做快取的東西很多,我們可以對資料介面進行快取,還可以對WebView進行快取,還有圖片快取、高度快取等等;

在這裡不得不說一下,資料儲存了;我們在做快取的時候選擇正確合適的儲存方式也很難重要,當然大家也可以根據專案的實際需求來選擇儲存,我們公司的APP本地儲存我選擇的是SQLite,管理類是我基於FMDB的又一次封裝.

網路API最佳化
這個最佳化不單單需是前端小夥伴的事,還需要後端開發人員的配合,避免一些不必要資料的返回,更要避免在APP端處理或者計算太多東西,比如年齡、星座的計算;即使後端的小夥伴把一些不必要的資料返回了,我們在建立資料模型Model的時候可以選擇不去接收!

PS:這裡我需要吐槽一下,以前公司有個java後端,返回的資料全部是一個表的實體類,管你有用沒用,直接一股腦的全給你,自己去計算和自己去查詢,那真叫一個心累啊,所以一個優秀的API開發工程師也是一個非常重要的原因!

自動釋放池
自動釋放池(autoreleasepool)在MRC的時代真是用的非常多,但是現在的專案都是ARC,自動釋放池用的也就比較少了,因為有系統幫我們監管,但是如果我們一個頁面建立了太多的類或者物件,如果等頁面銷燬的時候由系統統一釋放難免會出現一個峰值影響整體效能,這時候我們就可以考慮使用autoreleasepool了,避免峰值的出現!?

還比如我們在遍歷一些大陣列或者字典的時候,可以使用自動釋放池來降低記憶體峰值,比如:

NSArray *bigArray = @[] //這是一個很大很大的陣列
NSMutableArray *newArray = [[NSMutableArray alloc] init];
for (NSStirng *item in bigArray) {
@autoreleasepool {
ZFJModel *model = [[ZFJModel alloc] init];
model.item = item;
[newArray addObject: model];
}
}
APP啟動最佳化
在AppDelegate裡面我們會寫一些很多東西,你寫的東西越多越影響APP的冷啟動時間就會越長,因此我們需要對AppDelegate進行減負,具體如何減負大家可以根據各自專案的實際情況,第一可以刪除一些不是必須的東西;第二可以把一些東西寫在別的地方(如RootViewController),我在APP的RootViewController寫的東西比如IM聊天的配置初始化登入,一些未處理的任務,還有一些版本的堅持和快取的清理等等!

Instruments
Instruments 是什麼我這裡就不作過多的介紹了,這裡我主要說我用Instruments幹什麼;我平時用Instruments主要幹兩件事,一件事是檢查記憶體洩漏,還有一件事是檢查耗時函式;對於記憶體洩漏我們對應修改補漏就行了,對於耗時函式要麼就換一種寫法,要麼就建立一個子執行緒來處理!

?這裡要說一個我的習慣,我的所有的控制器都繼承於BaseViewController,然後我再BaseViewController裡面寫了以下的一部分程式碼:

  • (void)dealloc{
    NSLog(@"=== %@ dealloced! ===", NSStringFromClass([self class]));
    }
    如果我的頁面銷燬不走dealloc的列印,那麼頁面肯定有沒有被釋放的物件,要麼是發生迴圈引用了,要麼是需要手動釋放,具體列印以下物件的引用計數!

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3549/viewspace-2797934/,如需轉載,請註明出處,否則將追究法律責任。

相關文章