iOS 性能優(yōu)化(I)



1. 如何對iOS設(shè)備進行性能測試?

Xcode->Open Developer Tool -> Instruments ->Time Profiler

測試介紹:App耗時方法檢測



2. 開發(fā)項目時你是怎么檢查內(nèi)存泄露?

1). 靜態(tài)分析 analyze功氨。
2). instruments工具里面有個leak可以動態(tài)分析祟滴。



3. UITableView 如何進行優(yōu)化?


參考資料:tableView懶加載圖片, tableview的優(yōu)化,異步繪制cell绿饵,UITableViewCell 性能優(yōu)化



4. 什么是懶加載欠肾?

答:懶加載就是只在用到的時候才去初始化。也可以理解成延時加載拟赊。
我覺得最好也最簡單的一個例子就是tableView中圖片的加載顯示了, 一個延時加載, 避免內(nèi)存過高,一個異步加載,避免線程堵塞提高用戶體驗刺桃。



5. UITableView重用機制?

UITableView 通過重用單元格來達到節(jié)省內(nèi)存的目的: 通過為每個單元格指定一個重用標(biāo)識符吸祟,即指定了單元格的種類,當(dāng)屏幕上的單元格滑出屏幕時瑟慈,系統(tǒng)會把這個單元格添加到重用隊列中,等待被重用屋匕,當(dāng)有新單元格從屏幕外滑入屏幕內(nèi)時葛碧,從重用隊列中找看有沒有可以重用的單元格,如果有过吻,就拿過來用进泼,如果沒有就創(chuàng)建一個來使用。



6. 如何高性能的給 UIImageView 加個圓角?
不好的解決方案:使用下面的方式會強制Core Animation提前渲染屏幕的離屏繪制, 而離屏繪制就會給性能帶來負(fù)面影響疮装,會有卡頓的現(xiàn)象出現(xiàn)缘琅。

self.view.layer.cornerRadius = 5.0f;
self.view.layer.masksToBounds = YES;

正確的解決方案:使用繪圖技術(shù)

- (UIImage *)circleImage {
    // NO代表透明
    UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
    // 獲得上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 添加一個圓
    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
    CGContextAddEllipseInRect(ctx, rect);
    // 裁剪
    CGContextClip(ctx);
    // 將圖片畫上去
    [self drawInRect:rect];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    // 關(guān)閉上下文
    UIGraphicsEndImageContext();
    return image;
}

還有一種方案:使用了貝塞爾曲線"切割"個這個圖片, 給UIImageView 添加了的圓角,其實也是通過繪圖技術(shù)來實現(xiàn)的廓推。

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
imageView.center = CGPointMake(200, 300);
UIImage *anotherImage = [UIImage imageNamed:@"image"];
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds
                       cornerRadius:50] addClip];
[anotherImage drawInRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.view addSubview:imageView];



7. 談?wù)?UITableView 的優(yōu)化

1). 正確的復(fù)用cell刷袍。
2). 設(shè)計統(tǒng)一規(guī)格的Cell
3). 提前計算并緩存好高度(布局),因為heightForRowAtIndexPath:是調(diào)用最頻繁的方法樊展;
4). 異步繪制呻纹,遇到復(fù)雜界面堆生,遇到性能瓶頸時,可能就是突破口雷酪;
4). 滑動時按需加載淑仆,這個在大量圖片展示,網(wǎng)絡(luò)加載的時候很管用哥力!
5). 減少子視圖的層級關(guān)系
6). 盡量使所有的視圖不透明化以及做切圓操作蔗怠。
7). 不要動態(tài)的add 或者 remove 子控件。最好在初始化時就添加完吩跋,然后通過hidden來控制是否顯示寞射。
8). 使用調(diào)試工具分析問題。



8. 如何實行cell的動態(tài)的行高

如果希望每條數(shù)據(jù)顯示自身的行高锌钮,必須設(shè)置兩個屬性桥温,1.預(yù)估行高,2.自定義行高梁丘。
設(shè)置預(yù)估行高 tableView.estimatedRowHeight = 200侵浸。
設(shè)置定義行高 tableView.estimatedRowHeight = UITableViewAutomaticDimension。 
如果要讓自定義行高有效氛谜,必須讓容器視圖有一個自下而上的約束掏觉。



9. 內(nèi)存管理有哪幾種?
Objective-C的內(nèi)存管理主要有三種方式ARC(自動內(nèi)存計數(shù))混蔼、手動內(nèi)存計數(shù)履腋、內(nèi)存池珊燎。

1). 自動內(nèi)存計數(shù)ARC:由Xcode自動在App編譯階段惭嚣,在代碼中添加內(nèi)存管理代碼。
2). 手動內(nèi)存計數(shù)MRC:遵循內(nèi)存誰申請悔政、誰釋放晚吞;誰添加,誰釋放的原則谋国。
3). 內(nèi)存釋放池Release Pool:把需要釋放的內(nèi)存統(tǒng)一放在一個池子中槽地,當(dāng)池子被抽干后(drain),池子中所有的內(nèi)存空間也被自動釋放掉芦瘾。內(nèi)存池的釋放操作分為自動和手動捌蚊。自動釋放受runloop機制影響。

有一個很經(jīng)典的面試題近弟,考察自動釋放池的如下:

for (int i = 0; i < MAXFLOAT; i++) {

        NSString *string = @"stdy";
        string = [string lowercaseString];
        string = [string stringByAppendingString:@"123"];
        NSLog(@"--%@", string);
    }

? 上述的這種寫法缅糟,會使內(nèi)存慢慢增加,如何解決呢祷愉,面試官想要的答案就是用自動釋放池窗宦,你也可以改成其他的赦颇,但不是面試官要的,你懂的赴涵,修改如下:

for (int i = 0; i < MAXFLOAT; i++) {
        @autoreleasepool {
            NSString *string = @"stdy";
            string = [string lowercaseString];
            string = [string stringByAppendingString:@"123"];
            NSLog(@"--%@", string);
        }
    }

9.1 什么時間會創(chuàng)建自動釋放池媒怯?

? 從程序啟動到加載完成是一個完整的運行循環(huán),然后會停下來髓窜,等待用戶交互扇苞,用戶的每一次交互都會啟動一次運行循環(huán),來處理用戶所有的點擊事件寄纵、觸摸事件杨拐,運行循環(huán)檢測到事件并啟動后,就會創(chuàng)建自動釋放池擂啥。
? 子線程的 runloop 默認(rèn)是不工作哄陶,無法主動創(chuàng)建,必須手動創(chuàng)建哺壶。自定義的 NSOperation 和 NSThread 需要手動創(chuàng)建自動釋放池屋吨。比如:自定義的 NSOperation 類中的 main 方法里就必須添加自動釋放池。否則出了作用域后山宾,自動釋放對象會因為沒有自動釋放池去處理它至扰,而造成內(nèi)存泄露。

? 但對于 blockOperationinvocationOperation 這種默認(rèn)的Operation 资锰,系統(tǒng)已經(jīng)幫我們封裝好了敢课,不需要手動創(chuàng)建自動釋放池。
@autoreleasepool 當(dāng)自動釋放池被銷毀或者耗盡時绷杜,會向自動釋放池中的所有對象發(fā)送 release 消息直秆,釋放自動釋放池中的所有對象。
如果在一個vc的viewDidLoad中創(chuàng)建一個 Autorelease對象鞭盟,那么該對象會在 viewDidAppear 方法執(zhí)行前就被銷毀了圾结。



10. 什么會造成離屏渲染?

GPU屏幕渲染有兩種方式:
(1)On-Screen Rendering (當(dāng)前屏幕渲染)
指的是GPU的渲染操作是在當(dāng)前用于顯示的屏幕緩沖區(qū)進行齿诉。
(2)Off-Screen Rendering (離屏渲染)
指的是在GPU在當(dāng)前屏幕緩沖區(qū)以外開辟一個緩沖區(qū)進行渲染操作筝野。

下面的情況或操作會引發(fā)離屏渲染:

  • 為圖層設(shè)置遮罩(layer.mask)
  • 將圖層的layer.masksToBounds / view.clipsToBounds屬性設(shè)置為true
  • 將圖層layer.allowsGroupOpacity屬性設(shè)置為YES和layer.opacity小于1.0
  • 為圖層設(shè)置陰影(layer.shadow *)。
  • 為圖層設(shè)置layer.shouldRasterize=true
  • 具有l(wèi)ayer.cornerRadius粤剧,layer.edgeAntialiasingMask歇竟,layer.allowsEdgeAntialiasing的圖層
  • 文本(任何種類,包括UILabel抵恋,CATextLayer焕议,Core Text等)拔鹰。
  • 使用CGContext在drawRect :方法中繪制大部分情況下會導(dǎo)致離屏渲染浴捆,甚至僅僅是一個空的實現(xiàn)。


優(yōu)化方案

1). 圓角優(yōu)化
方案1 :使用貝塞爾曲線UIBezierPath和Core Graphics框架畫出一個圓角
方案2 :使用CAShapeLayer和UIBezierPath設(shè)置圓角

2). shadow優(yōu)化
對于shadow,如果圖層是個簡單的幾何圖形或者圓角圖形捍歪,我們可以通過設(shè)置shadowPath來優(yōu)化性能雹食,能大幅提高性能


其他優(yōu)化

? 當(dāng)我們需要圓角效果時瓦哎,可以使用一張中間透明圖片蒙上去使用ShadowPath指定layer陰影效果路徑唉韭;
? 使用異步進行l(wèi)ayer渲染(Facebook開源的異步繪制框架AsyncDisplayKit)設(shè)置layer的opaque值為YES,減少復(fù)雜圖層合成盡量使用不包含透明(alpha)通道的圖片資源;
? 盡量設(shè)置layer的大小值為整形值畜隶;
? 直接讓美工把圖片切成圓角進行顯示壁肋,這是效率最高的一種方案很多情況下用戶上傳圖片進行顯示,可以讓服務(wù)端處理圓角使用代碼手動生成圓角Image設(shè)置到要顯示的View上籽慢;
? 利用UIBezierPath(CoreGraphics框架)畫出來圓角圖片



11. 說一下NSTimer在使用的時候內(nèi)存泄漏的分析?

NSTimer必須與RunLoop搭配使用浸遗,因為其定時任務(wù)的觸發(fā)基于RunLoop。
NSTimer使用常見的Target-Action模式箱亿。由于RunLoop會強引用timer跛锌,timer會強引用Target,容易造成循環(huán)引用届惋、內(nèi)存泄露等問題髓帽。



12. loop 強引用timer, timer 強引用 target,如果不能釋放脑豹,會造成內(nèi)存泄漏郑藏,有一個面試官問如果在target中傳入weak的self,那么可以解決循環(huán)引用問題嗎瘩欺?

答案:否

? Target強引用or弱引用Timer并不是問題的關(guān)鍵必盖,問題的關(guān)鍵是:一定要在Timer使用完畢調(diào)用invalidate使之失效(手動調(diào)用or系統(tǒng)自動調(diào)用),Timer從RunLoop中被移除并清除強引用俱饿,這個操作可打破引用1歌粥、2,而引用3是強弱引用已經(jīng)不重要了稍途。

? NSTimer一共有三種初始化方案:init開頭的普通創(chuàng)建方法阁吝、timer開頭的類工廠方法、scheduled開頭的類工廠方法械拍。前兩者需要手動加入RunLoop中,后者會自動加入當(dāng)前RunLoop的DefaultMode中装盯。



13. 對于NSTimer坷虑,面試官還會問,它是否是時間準(zhǔn)確呢埂奈?
? 大家可能都知道是時間不準(zhǔn)確的迄损,因為受RunLoop的影響,那么GCD中也有延時账磺,如果用GCD來做延時芹敌,那時間準(zhǔn)確嗎痊远?

GCD的time是準(zhǔn)確的,GCD 的線程管理是通過系統(tǒng)來直接管理的氏捞。
GCD Timer 是通過 dispatch port 給 RunLoop 發(fā)送消息碧聪,來使 RunLoop 執(zhí)行相應(yīng)的 block。
如果所在線程沒有 RunLoop液茎,那么 GCD 會臨時創(chuàng)建一個線程去執(zhí)行 block逞姿,執(zhí)行完之后再銷毀掉,因此 GCD 的 Timer 是不依賴 RunLoop 的捆等。



14. 大次數(shù)循環(huán)內(nèi)存暴漲問題


for (int i = 0; i < 100000; i++) {
  NSString *string = @“Abc”;
  string = [string lowercaseString];
  string = [string stringByAppendingString:@“xyz”];
  NSLog(@"%@", string);
}
改:
for (int i = 0; i < 100000; i++) {
  @autoreleasepool {
    NSString *string = @“Abc”;
    string = [string lowercaseString];
    string = [string stringByAppendingString:@“xyz”];
    NSLog(@"%@", string);
  }
}




15. 內(nèi)存泄漏可能會出現(xiàn)的幾種原因滞造,聊聊你的看法?
15.1 非OC對象如何處理栋烤?
15.2 地圖類內(nèi)存若泄漏谒养,如何處理?
15.3 若常用框架出現(xiàn)內(nèi)存泄漏如何處理明郭?
iOS 出現(xiàn)內(nèi)存泄漏的幾種原因



``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``




``


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蝴光,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子达址,更是在濱河造成了極大的恐慌蔑祟,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沉唠,死亡現(xiàn)場離奇詭異疆虚,居然都是意外死亡,警方通過查閱死者的電腦和手機满葛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門径簿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嘀韧,你說我怎么就攤上這事篇亭。” “怎么了锄贷?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵译蒂,是天一觀的道長。 經(jīng)常有香客問我谊却,道長柔昼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任炎辨,我火速辦了婚禮捕透,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己乙嘀,他們只是感情好末购,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著虎谢,像睡著了一般盟榴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上嘉冒,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天曹货,我揣著相機與錄音,去河邊找鬼讳推。 笑死顶籽,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的银觅。 我是一名探鬼主播礼饱,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼究驴!你這毒婦竟也來了镊绪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤蝴韭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后熙侍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡蛉抓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了巷送。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡笑跛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出堡牡,到底是詐尸還是另有隱情抒抬,我是刑警寧澤晤柄,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布妖胀,位于F島的核電站惠勒,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏爬坑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一盾计、第九天 我趴在偏房一處隱蔽的房頂上張望售担。 院中可真熱鬧,春花似錦署辉、人聲如沸族铆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽哥攘。三九已至,卻和暖如春材鹦,著一層夾襖步出監(jiān)牢的瞬間逝淹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工桶唐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留栅葡,地道東北人。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓尤泽,卻偏偏與公主長得像欣簇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子安吁,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351