潭州ios面試題6 必看

1 NSOperationQueue和GCD的區(qū)別是什么

GCD(Grand Central Dispatch)是底層的C語(yǔ)言構(gòu)成的API,而NSOperationQueue及相關(guān)對(duì)象是Objc的對(duì)象。在GCD中,在隊(duì)列中執(zhí)行的是由block構(gòu)成的任務(wù),這是一個(gè)輕量級(jí)的數(shù)據(jù)結(jié)構(gòu);NSOperation是一個(gè)抽象類,它封裝了線程的細(xì)節(jié)實(shí)現(xiàn),我們可以通過子類化該對(duì)象,加上NSQueue來同面向?qū)ο蟮乃季S,管理多線程程序。而Operation為我們提供了更多的選擇;

2.在NSOperationQueue中,我們可以隨時(shí)取消已經(jīng)設(shè)定要準(zhǔn)備執(zhí)行的任務(wù)(當(dāng)然,已經(jīng)開始的任務(wù)就無法阻止了),而GCD沒法停止已經(jīng)加入queue的block(其實(shí)是有的,但需要許多復(fù)雜的代碼);

3.NSOperation能夠方便地設(shè)置依賴關(guān)系,我們可以讓一個(gè)Operation依賴于另一個(gè)Operation,這樣的話盡管兩個(gè)Operation處于同一個(gè)并行隊(duì)列中,但前者會(huì)直到后者執(zhí)行完畢后再執(zhí)行;

4.我們能將KVO應(yīng)用在NSOperation中,可以監(jiān)聽一個(gè)Operation是否完成或取消,這樣子能比GCD更加有效地掌控我們執(zhí)行的后臺(tái)任務(wù);

5.在NSOperation中,我們能夠設(shè)置NSOperation的priority優(yōu)先級(jí),能夠使同一個(gè)并行隊(duì)列中的任務(wù)區(qū)分先后地執(zhí)行,而在GCD中,我們只能區(qū)分不同任務(wù)隊(duì)列的優(yōu)先級(jí),如果要區(qū)分block任務(wù)的優(yōu)先級(jí),也需要大量的復(fù)雜代碼;

6.我們能夠?qū)SOperation進(jìn)行繼承,在這之上添加成員變量與成員方法,提高整個(gè)代碼的復(fù)用度,這比簡(jiǎn)單地將block任務(wù)排入執(zhí)行隊(duì)列更有自由度,能夠在其之上添加更多自定制的功能。

7.GCD?是嚴(yán)格的隊(duì)列,先進(jìn)先出FIFO;NSOperation可以改動(dòng)?優(yōu)先級(jí)(或者說服務(wù)質(zhì)量)改變執(zhí)行順序

8.NSOperation的高級(jí):最大并發(fā)數(shù),控制線程個(gè)數(shù),優(yōu)化了線程的暫停、繼續(xù)、取消功能(GCD實(shí)現(xiàn)起來太難,可以用?KVO?),依賴關(guān)系,可以讓異步任務(wù)

同步執(zhí)行.

2、GCD與NSThread的區(qū)別: ?

1).?NSThread?通過?@selector?指定要執(zhí)行的方法,代碼分散,?依靠的是NSObject的分類實(shí)現(xiàn)的線程之間的通訊,如果要開線程必須創(chuàng)建多個(gè)線程對(duì)象。經(jīng)常只用的是[NSTread current]?查看當(dāng)前的線程。

NSThread是一個(gè)控制線程執(zhí)行的對(duì)象,它不如NSOperation抽象,通過它我們可以方便的得到一個(gè)線程,并控制它。但NSThread的線程之間的并發(fā)控制,是需要我們自己來控制的,可以通過NSCondition實(shí)現(xiàn)。

?2).GCD 通過?block指定要執(zhí)行的代碼,代碼集中, 所有的代碼寫在一起的,讓代碼更加簡(jiǎn)單,易于閱讀和維護(hù),不需要管理線程的創(chuàng)建/銷毀/復(fù)用的過程!程序員不用關(guān)心線程的生命周期

3?利用NSOperation與NSOperationqueue處理多線程時(shí),有3個(gè)NSOperation分別為A,B,C,要求A,B執(zhí)行完畢后,在執(zhí)行C,如何做?

有三種實(shí)現(xiàn)方案

方案一:串行隊(duì)列同步執(zhí)行(GCD實(shí)現(xiàn))

A,B放在串行隊(duì)列中執(zhí)行,執(zhí)行完畢,主隊(duì)列執(zhí)行任務(wù)C

方案二:隊(duì)列依賴實(shí)現(xiàn)

方案三:并發(fā)隊(duì)列和主隊(duì)列實(shí)現(xiàn)

http://www.itdecent.cn/p/0b0d9b1f1f19

多線程

一.資源搶奪

2>?資源搶奪解決方案

@sychronized{ }

dispatch_barrier_async

NSLock NSCondition

dispatch_semaphore_wait

4+(void)load與+(void)initialize區(qū)別

?load方法:

?1.當(dāng)類加載到OC運(yùn)行時(shí)環(huán)境(內(nèi)存)中的時(shí)候,就會(huì)調(diào)用一次(一個(gè)類只會(huì)加載一次).

?2. 程序一啟動(dòng)就會(huì)調(diào)用.

?3.程序運(yùn)行過程中,只會(huì)調(diào)用1次.

?initialize方法:

?1.當(dāng)?shù)谝淮问褂眠@個(gè)類的時(shí)候(比如調(diào)用了類的某個(gè)方法)才會(huì)調(diào)用.

?2. 并非程序一啟動(dòng)就會(huì)調(diào)用.

load和initialize的共同特點(diǎn)

在不考慮開發(fā)者主動(dòng)使用的情況下,系統(tǒng)最多會(huì)調(diào)用一次

?如果父類和子類都被調(diào)用,父類的調(diào)用一定在子類之前

都是為了應(yīng)用運(yùn)行提前創(chuàng)建合適的運(yùn)行環(huán)境

?在使用時(shí)都不要過重地依賴于這兩個(gè)方法,除非真正必要

?它們的相同點(diǎn)在于:方法只會(huì)被調(diào)用一次。(其實(shí)這是相對(duì)runtime來說的,后邊會(huì)做進(jìn)一步解釋)。

??load是只要類所在文件被引用就會(huì)被調(diào)用,而initialize是在類或者其子類的第一個(gè)方法被調(diào)用前調(diào)用。所以如果類沒有被引用進(jìn)項(xiàng)目,就不會(huì)有l(wèi)oad調(diào)用;但即使類文件被引用進(jìn)來,但是沒有使用,那么initialize也不會(huì)被調(diào)用。

?文檔也明確闡述了方法調(diào)用的順序:父類(Superclass)的方法優(yōu)先于子類(Subclass)的方法,類中的方法優(yōu)先于類別(Category)中的方法。

5?說說關(guān)于UDP/TCP的區(qū)別?

UDP(User Data Protocol,用戶數(shù)據(jù)報(bào)協(xié)議)是與TCP相對(duì)應(yīng)的協(xié)議。它是面向非連接的協(xié)議,它不與對(duì)方建立連接,而是直接就把數(shù)據(jù)包發(fā)送過去! UDP適用于一次只傳送少量數(shù)據(jù)、對(duì)可靠性要求不高的應(yīng)用環(huán)境。

1.只管發(fā)送,不確認(rèn)對(duì)方是否接收到

2.將數(shù)據(jù)及源和目的封裝成數(shù)據(jù)包中,不需要建立連接

3.每個(gè)數(shù)據(jù)報(bào)的大小限制在64K之內(nèi)

4.因?yàn)闊o需連接,因此是不可靠協(xié)議

5.不需要建立連接,速度快

應(yīng)用場(chǎng)景:多媒體教室/網(wǎng)絡(luò)流媒體

TCP(Transmission Control Protocol,傳輸控制協(xié)議)是基于連接的協(xié)議,也就是說,在正式收發(fā)數(shù)據(jù)前,必須和對(duì)方建立可靠的連接。一個(gè)TCP連接必須要經(jīng)過三次“對(duì)話”才能 建立起來,其中的過程非常復(fù)雜,我們這里只做簡(jiǎn)單、形象的介紹,你只要做到能夠理解這個(gè)過程即可。

1.建立連接,形成傳輸數(shù)據(jù)的通道

2.在連接中進(jìn)行大數(shù)據(jù)傳輸(數(shù)據(jù)大小不收限制)

3.通過三次握手完成連接,是可靠協(xié)議,安全送達(dá)

4.必須建立連接,效率會(huì)稍低

TCP傳輸控制協(xié)議主要包含下列任務(wù)和功能:

*?確保IP數(shù)據(jù)報(bào)的成功傳遞。

*?對(duì)程序發(fā)送的大塊數(shù)據(jù)進(jìn)行分段和重組。

*?確保正確排序及按順序傳遞分段的數(shù)據(jù)。

*?通過計(jì)算校驗(yàn)和,進(jìn)行傳輸數(shù)據(jù)的完整性檢查。

簡(jiǎn)單的說,TCP注重?cái)?shù)據(jù)安全,而UDP數(shù)據(jù)傳輸快點(diǎn),但安全性一般

3> TCPUDP的區(qū)別:

3.1>基于連接與無連接;

3.2>對(duì)系統(tǒng)資源的要求(TCP較多,UDP少);

3.3>UDP程序結(jié)構(gòu)較簡(jiǎn)單;

3.4>流模式與數(shù)據(jù)報(bào)模式;

3.5>TCP保證數(shù)據(jù)正確性,UDP可能丟包,TCP保證數(shù)據(jù)順序,UDP不保證

6?長(zhǎng)鏈接&短鏈接

?TCP短連接

?我們模擬一下TCP短連接的情況,client向server發(fā)起連接請(qǐng)求,server接到請(qǐng)求,然后雙方建立連接。client向server?發(fā)送消息,server回應(yīng)client,然后一次讀寫就完成了,這時(shí)候雙方任何一個(gè)都可以發(fā)起close操作,不過一般都是client先發(fā)起?close操作。為什么呢,一般的server不會(huì)回復(fù)完client后立即關(guān)閉連接的,當(dāng)然不排除有特殊的情況。從上面的描述看,短連接一般只會(huì)在 client/server間傳遞一次讀寫操作

?TCP長(zhǎng)連接

?接下來我們?cè)倌M一下長(zhǎng)連接的情況,client向server發(fā)起連接,server接受client連接,雙方建立連接。Client與server完成一次讀寫之后,它們之間的連接并不會(huì)主動(dòng)關(guān)閉,后續(xù)的讀寫操作會(huì)繼續(xù)使用這個(gè)連接。

?長(zhǎng)連接短連接操作過程

短連接的操作步驟是:

建立連接——數(shù)據(jù)傳輸——關(guān)閉連接…建立連接——數(shù)據(jù)傳輸——關(guān)閉連接

?長(zhǎng)連接的操作步驟是:

??建立連接——數(shù)據(jù)傳輸…(保持連接)…數(shù)據(jù)傳輸——關(guān)閉連接

?什么時(shí)候用長(zhǎng)連接,短連接?

?長(zhǎng)連接多用于操作頻繁,點(diǎn)對(duì)點(diǎn)的通訊,而且連接數(shù)不能太多情況,。每個(gè)TCP連接都需要三步握手,這需要時(shí)間,如果每個(gè)操作都是先連接,再操作的話 那么處理速度會(huì)降低很多,所以每個(gè)操作完后都不斷開,次處理時(shí)直接發(fā)送數(shù)據(jù)包就OK了,不用建立TCP連接。例如:數(shù)據(jù)庫(kù)的連接用長(zhǎng)連接, 如果用短連接頻繁的通信會(huì)造成socket錯(cuò)誤,而且頻繁的socket?創(chuàng)建也是對(duì)資源的浪費(fèi)。

長(zhǎng)連接和短連接的優(yōu)點(diǎn)和缺點(diǎn)

?由上可以看出,長(zhǎng)連接可以省去較多的TCP建立和關(guān)閉的操作,減少浪費(fèi),節(jié)約時(shí)間。對(duì)于頻繁請(qǐng)求資源的客戶來說,較適用長(zhǎng)連接。不過這里存在一個(gè)問 題,存活功能的探測(cè)周期太長(zhǎng),還有就是它只是探測(cè)TCP連接的存活,屬于比較斯文的做法,遇到惡意的連接時(shí),保活功能就不夠使了。在長(zhǎng)連接的應(yīng)用場(chǎng)景 下,client端一般不會(huì)主動(dòng)關(guān)閉它們之間的連接,Client與server之間的連接如果一直不關(guān)閉的話,會(huì)存在一個(gè)問題,隨著客戶端連接越來越 多,server早晚有扛不住的時(shí)候,這時(shí)候server端需要采取一些策略,如關(guān)閉一些長(zhǎng)時(shí)間沒有讀寫事件發(fā)生的連接,這樣可 以避免一些惡意連接導(dǎo)致server端服務(wù)受損;如果條件再允許就可以以客戶端機(jī)器為顆粒度,限制每個(gè)客戶端的最大長(zhǎng)連接數(shù),這樣可以完全避免某個(gè)蛋疼的 客戶端連累后端服務(wù)。

?短連接對(duì)于服務(wù)器來說管理較為簡(jiǎn)單,存在的連接都是有用的連接,不需要額外的控制手段。但如果客戶請(qǐng)求頻繁,將在TCP的建立和關(guān)閉操作上浪費(fèi)時(shí)間和帶寬。

長(zhǎng)連接和短連接的產(chǎn)生在于client和server采取的關(guān)閉策略,具體的應(yīng)用場(chǎng)景采用具體的策略,沒有十全十美的選擇,只有合適的選擇。


7 TCP?傳輸原理

1>TCP如何防止亂序和丟包

???TCP數(shù)據(jù)包的頭格式中有兩個(gè)概念,Sequence Number是數(shù)據(jù)包的序號(hào),用來解決網(wǎng)絡(luò)包亂序(reordering)問題。Acknowledgement Number就是ACK——用于確認(rèn)收到,用來解決不丟包的問題。

???位碼即tcp標(biāo)志位,有6種標(biāo)示:SYN(synchronous建立聯(lián)機(jī))?ACK(acknowledgement?確認(rèn)) PSH(push傳送) FIN(finish結(jié)束) RST(reset重置) URG(urgent緊急)Sequence number(順序號(hào)碼) Acknowledge number(確認(rèn)號(hào)碼).

??SeqNum的增加是和傳輸?shù)淖止?jié)數(shù)相關(guān)的,TCP傳輸數(shù)據(jù)時(shí),A主機(jī)第一次傳輸1440個(gè)字節(jié),seq=1,那么第二次時(shí)seq = 1441,B拼接數(shù)據(jù)就是根據(jù)seq進(jìn)行拼接的,seq數(shù)字不斷累加避免了亂序.B主機(jī)收到第一次數(shù)據(jù)包以后會(huì)返回ack = 1441.

???A主機(jī)收到B的ack = 1441時(shí),就知道第一個(gè)數(shù)據(jù)包B已收到.?如果B沒有收到第一次的數(shù)據(jù)包,那么B再收到A的數(shù)據(jù)包時(shí),他就會(huì)發(fā)ack = 1回去,A收到B的回復(fù),發(fā)現(xiàn)B沒有收到第一次數(shù)據(jù)包,就會(huì)重發(fā)第一次數(shù)據(jù)包,這樣就可以防止丟包.


2>描述一下三次握手


??第一次握手:建立連接時(shí),客戶端發(fā)送syn包(syn=j)到服務(wù)器,并進(jìn)入SYN_SEND狀態(tài),等待服務(wù)器確認(rèn);

??第二次握手:服務(wù)器收到syn包,必須確認(rèn)客戶的SYN(ack=j+1),同時(shí)自己也發(fā)送一個(gè)SYN包(syn=k),即SYN+ACK包,此時(shí)服務(wù)器進(jìn)入SYN_RECV狀態(tài);

??第三次握手:客戶端收到服務(wù)器的SYN+ACK包,向服務(wù)器發(fā)送確認(rèn)包ACK(ack=k+1),此包發(fā)送完畢,客戶端和服務(wù)器進(jìn)入ESTABLISHED狀態(tài),完成三次握手。完成三次握手,客戶端與服務(wù)器開始傳送數(shù)據(jù).

3>三次握手實(shí)現(xiàn)的過程:

第一次握手:建立連接時(shí),客戶端發(fā)送同步序列編號(hào)到服務(wù)器,并進(jìn)入發(fā)送狀態(tài),等待服務(wù)器確認(rèn)

第二次:服務(wù)器收到同步序列編號(hào),并確認(rèn)同時(shí)自己也發(fā)送一個(gè)同步序列編號(hào)+確認(rèn)標(biāo)志,此時(shí)服務(wù)器進(jìn)入接收狀態(tài)

第三次:客戶端收到服務(wù)器發(fā)送的包,并向服務(wù)器發(fā)送確認(rèn)標(biāo)志,隨后鏈接成功。

注意:是在鏈接成功后在進(jìn)行數(shù)據(jù)傳輸。

8?.http和scoket通信的區(qū)別?socket連接相關(guān)庫(kù),TCP,UDP的連接方法,HTTP的幾種常用方式?

http和scoket通信的區(qū)別:

http是客戶端用http協(xié)議進(jìn)行請(qǐng)求,發(fā)送請(qǐng)求時(shí)候需要封裝http請(qǐng)求頭,并綁定請(qǐng)求的數(shù)據(jù),服務(wù)器一般有web服務(wù)器配合(當(dāng)然也非絕對(duì))。?http請(qǐng)求方式為客戶端主動(dòng)發(fā)起請(qǐng)求,服務(wù)器才能給響應(yīng),一次請(qǐng)求完畢后則斷開連接,以節(jié)省資源。服務(wù)器不能主動(dòng)給客戶端響應(yīng)(除非采取http長(zhǎng)連接技術(shù))。iphone主要使用類是NSUrlSe on。?

scoket是客戶端跟服務(wù)器直接使用socket“套接字”進(jìn)行連接,并沒有規(guī)定連接后斷開,所以客戶端和服務(wù)器可以保持連接通道,雙方都可以主動(dòng)發(fā)送數(shù)據(jù)。一般在游戲開發(fā)或股票開發(fā)這種要求即時(shí)性很強(qiáng)并且保持發(fā)送數(shù)據(jù)量比較大的場(chǎng)合使用。主要使用類是CFSocketRef。

9?BLOCK

使用block時(shí)什么情況會(huì)發(fā)生引用循環(huán),如何解決?

一個(gè)對(duì)象中強(qiáng)引用了block,在block中又使用了該對(duì)象,就會(huì)發(fā)射循環(huán)引用。 解決方法是將該對(duì)象使用__weak或者_(dá)_block修飾符修飾之后再在block中使用。

id weak weakSelf = self;?或者weak __typeof(&*self)weakSelf = self該方法可以設(shè)置宏

id __block weakSelf = self;

事后解除指控

10?ivar、getter、setter 是如何生成并添加到這個(gè)類中的?

“自動(dòng)合成”( autosynthesis)

完成屬性定義后,編譯器會(huì)自動(dòng)編寫訪問這些屬性所需的方法,此過程叫做“自動(dòng)合成”(autosynthesis)。需要強(qiáng)調(diào)的是,這個(gè)過程由編譯 器在編譯期執(zhí)行,所以編輯器里看不到這些“合成方法”(synthesized method)的源代碼。除了生成方法代碼?getter、setter 之外,編譯器還要自動(dòng)向類中添加適當(dāng)類型的實(shí)例變量,并且在屬性名前面加下劃線,以此作為實(shí)例變量的名字。在前例中,會(huì)生成兩個(gè)實(shí)例變量,其名稱分別為_firstName與?_lastName。也可以在類的實(shí)現(xiàn)代碼里通過?@synthesize?語(yǔ)法來指定實(shí)例變量的名字.

11?@protocol和?category?中如何使用@property

在?protocol?中使用?property?只會(huì)生成setter 和?getter?方法聲明,我們使用屬性的目的,是希望遵守我協(xié)議的對(duì)象能實(shí)現(xiàn)該屬性

category使用?@property?也是只會(huì)生成 setter 和?getter?方法的聲明,如果我們真的需要給?category?增加屬性的實(shí)現(xiàn),需要借助于運(yùn)行時(shí)的兩個(gè)函數(shù):

objc_setAssociatedObject

objc_getAssociatedObject

12?.runtime如何通過selector找到對(duì)應(yīng)的IMP地址?

每一個(gè)類對(duì)象中都一個(gè)方法列表(isa),方法列表中記錄著方法的名稱,方法實(shí)現(xiàn),以及參數(shù)類型,其實(shí)selector本質(zhì)就是方法名稱,通過這個(gè)方法名稱就可以在方法列表中找到對(duì)應(yīng)的方法實(shí)現(xiàn).

13.TableView性能優(yōu)化

1.行高一定要緩存!

2.不要?jiǎng)討B(tài)創(chuàng)建子視圖

所有的子視圖都預(yù)先創(chuàng)建,如果不需要顯示可以設(shè)置?hidden

3.所有的子視圖都應(yīng)該添加到?contentView?上

4.所有的子視圖都必須指定背景顏色,且所有的顏色都不要使用 alpha

5.cell?柵格化

6.異步繪制

UITableView的優(yōu)化主要從三個(gè)方面入手:

1.提前計(jì)算并緩存好高度(布局),因?yàn)閔eightForRowAtIndexPath:是調(diào)用最頻繁的方法;

2.異步繪制,遇到復(fù)雜界面,遇到性能瓶頸時(shí),可能就是突破口;

3.滑動(dòng)時(shí)按需加載,這個(gè)在大量圖片展示,網(wǎng)絡(luò)加載的時(shí)候很管用?。⊿DWebImage已經(jīng)實(shí)現(xiàn)異步加載,配合這條性能杠杠的)。

正確使用reuseIdentifier來重用Cells

盡量使所有的view opaque,包括Cell自身

盡量少用或不用透明圖層

如果Cell內(nèi)現(xiàn)實(shí)的內(nèi)容來自web,使用異步加載,緩存請(qǐng)求結(jié)果

減少subviews的數(shù)量

在heightForRowAtIndexPath:中盡量不使用cellForRowAtIndexPath:,如果你需要用到它,只用一次然后緩存結(jié)果

盡量少用addView給Cell動(dòng)態(tài)添加View,可以初始化時(shí)就添加,然后通過hide來控制是否顯示

?在使用UITableView的時(shí)候,有的時(shí)候你會(huì)碰到Cell卡頓,圖片加載慢,使得滑動(dòng)cell時(shí)變得不那么流暢,這些都會(huì)影響用戶體驗(yàn),拉低整體app的效果。

1.???使用cell重用機(jī)制,盡可能快地返回重用cell實(shí)例? ?這點(diǎn)大家都應(yīng)該比較清楚,使用reuse機(jī)制能大幅降低創(chuàng)建cell所帶來的損耗,這就要各位在UITableView的dataSource中實(shí)現(xiàn)的tableView:cellForRowAtIndexPath:方法。只是有一點(diǎn),盡量不要在此時(shí)綁定數(shù)據(jù),因?yàn)槟壳霸谄聊簧线€沒有cell,可以在UITableView的delegate方法tableView:willDisplayCell:forRowAtIndexPath:中進(jìn)行數(shù)據(jù)的填充。 ? ? 需要說明的是,你可能會(huì)動(dòng)態(tài)計(jì)算cell高度,但最好是不要選擇Autolayout。使用Autolayout后,會(huì)根據(jù)cell的子視圖使得求解的約束也越多,從而降低計(jì)算速度,影響滑動(dòng)時(shí)FPS。所以,為了使tableview平滑滾動(dòng),請(qǐng)使用動(dòng)態(tài)計(jì)算高度,不要選擇Autolayout。

2. cell的subViews的各級(jí)opaque值要設(shè)成YES

?opaque用于輔助繪圖系統(tǒng),表示UIView是否透明。在不透明的情況下,渲染視圖時(shí)需要快速地渲染,以提高性能。渲染最慢的操作之一是混合(blending)。提高性能的方法是減少混合操作的次數(shù),其實(shí)就是GPU的不合理使用,這是硬件來完成的(混合操作由GPU來執(zhí)行,因?yàn)檫@個(gè)硬件就是用來做混合操作的,當(dāng)然不只是混合)。 優(yōu)化混合操作的關(guān)鍵點(diǎn)是在平衡CPU和GPU的負(fù)載。??

? ? ? ?還有就是cell的layer的shouldRasterize要設(shè)成YES。

3. 在繪制字符串時(shí),盡可能使用drawAtPoint:withFont: ,?在繪制圖片,盡量使用drawAtPoint

?? ?不要使用更復(fù)雜的drawAtPoint:(CGPoint)point forWidth:(CGFloat)width withFont:(UIFont*)font lineBreakMode:(UILineBreakMode)lineBreakMode;如果要繪制過長(zhǎng)的字符串,建議先截?cái)?,然后使用drawAtPoint:withFont:方法繪制。

??不要使用drawInRect, 因?yàn)樗诶L制過程中對(duì)圖片放縮大小,消耗CPU。

? ? ? ?其實(shí),最快的繪制就是你不要做任何繪制。有時(shí),通過UIGraphicsBeginImageContextWithOptions()或者CGBitmapContextCeate()創(chuàng)建位圖會(huì)顯得更有意義,從位圖上面抓取圖像,并設(shè)置為?CALayer?的內(nèi)容。

如果你必須實(shí)現(xiàn)?-drawRect:,并且你必須繪制大量的東西,這將占用時(shí)間。

圖片的話,你可能會(huì)用位圖來替代:

5. cell異步加載圖片以及緩存

?對(duì)于cell里的圖片采用異步的方式,加載好后緩存。當(dāng)圖片還沒有請(qǐng)求加載時(shí),你可以使用默認(rèn)圖片。

?一旦你緩存好圖片,使用cell的重用機(jī)制時(shí)就可以從關(guān)聯(lián)好的視圖源里以相應(yīng)的url來找到對(duì)應(yīng)的緩存圖片,緩存大大節(jié)省重復(fù)請(qǐng)求圖片的耗損。只是你要考慮內(nèi)存級(jí)別的緩存還是磁盤級(jí)別的緩存,記得使用完畢清緩存哦!(記得減少內(nèi)存級(jí)別的拷貝)

為了防止圖片多次下載,我們需要對(duì)圖片做緩存,緩存分為內(nèi)存緩存于沙盒緩存,我們當(dāng)然兩種都要實(shí)現(xiàn)。

般情況下在我們會(huì)在cellForRow方法里面設(shè)置cell的圖片數(shù)據(jù)源,也就是說如果一個(gè)cell的imageview對(duì)象開啟了一個(gè)下載任務(wù),這個(gè)時(shí)候該cell對(duì)象發(fā)生了重用,新的image數(shù)據(jù)源會(huì)開啟另外的一個(gè)下載任務(wù),由于他們關(guān)聯(lián)的imageview對(duì)象實(shí)際上是同一個(gè)cell實(shí)例的imageview對(duì)象,就會(huì)發(fā)生2個(gè)下載任務(wù)回調(diào)給同一個(gè)imageview對(duì)象。這個(gè)時(shí)候就有必要做一些處理,避免回調(diào)發(fā)生時(shí),錯(cuò)誤的image數(shù)據(jù)源刷新了UI。

在我們向下滑動(dòng)tableview的時(shí)候我們需要手動(dòng)去取消掉下載操作,當(dāng)用戶停止滑動(dòng),再去執(zhí)行下載操作

如果快速滑下去,然后又滑回來的話,圖片是過了一會(huì)才顯示出來,這是因?yàn)榭焖倩瑒?dòng)的時(shí)候,舊數(shù)據(jù)源的下載任務(wù)被取消掉了。

異步下載圖片我們用的是NSOperation,并且創(chuàng)建一個(gè)全局的queue來管理下載圖片的操作。

在把圖片顯示到Cell上之前

先判斷內(nèi)存中(images字典中)有沒有圖片,

如果有,則取出url對(duì)應(yīng)的圖片來顯示,

如果沒有,再去沙盒緩存中查看,當(dāng)然存到沙盒中都是NSData。

如果沙盒緩存中有,我們?nèi)〕鰧?duì)應(yīng)的數(shù)據(jù)給Cell去顯示

如果沙盒中也沒有圖片,我們先顯示占位圖片。再創(chuàng)建operation去執(zhí)行下載操作了。

當(dāng)然在創(chuàng)建operation之前,我們要判斷這個(gè)operation操作是否存在

如果沒有下載操作,我們才需要真正的去創(chuàng)建operation執(zhí)行下載。

創(chuàng)建好下載操作之后應(yīng)該把該操作存放到全局隊(duì)列中去異步執(zhí)行,同時(shí)吧操作放入operations字典中記錄下來。

下載完成之后:

把下載好的圖片放到內(nèi)存中、同時(shí)存到沙盒緩存中

執(zhí)行完上面的操作之后回到主線程刷新表格,

從operations字典中移除下載操作(防止operations越來越大,同時(shí)保證下載失敗后,能重新下載)

2、cell高度的計(jì)算

這邊我們分為兩種cell,一種是定高的cell,另外一種是動(dòng)態(tài)高度的cell。

(1)定高的cell,應(yīng)該采用如下方式:

self.tableView.rowHeight = 88;

這個(gè)方法指定了所有cell高度都是88的tableview,rowHeight默認(rèn)的值是44,所以一個(gè)空的TableView會(huì)顯示成這個(gè)樣子。對(duì)于定高cell,直接采用上面方式給定高度,不需要實(shí)現(xiàn)tableView:heightForRowAtIndexPath:以節(jié)省不必要的計(jì)算和開銷。

(2)動(dòng)態(tài)高度的cell

我們需要實(shí)現(xiàn)它的代理,來給出高度:


-(CGFloat)tableView:(UITableView *)tableViewheightForRowAtIndexPath:(NSIndexPath *)indexPath{


// return xxx

}

這個(gè)代理方法實(shí)現(xiàn)后,上面的rowHeight的設(shè)置將會(huì)變成無效。在這個(gè)方法中,我們需要提高cell高度的計(jì)算效率,來節(jié)省時(shí)間。

自從iOS8之后有了self-sizing cell的概念,cell可以自己算出高度,使用self-sizing cell需要滿足以下三個(gè)條件:

(1)使用Autolayout進(jìn)行UI布局約束(要求cell.contentView的四條邊都與內(nèi)部元素有約束關(guān)系)。

(2)指定TableView的estimatedRowHeight屬性的默認(rèn)值。

(3)指定TableView的rowHeight屬性為UITableViewAutomaticDimension。

除了提高cell高度的計(jì)算效率之外,對(duì)于已經(jīng)計(jì)算出的高度,我們需要進(jìn)行緩存,對(duì)于已經(jīng)計(jì)算過的高度,沒有必要進(jìn)行計(jì)算第二次。 ?


3、渲染

為了保證TableView的流暢,當(dāng)快速滑動(dòng)的時(shí)候,cell必須被快速的渲染出來。所以cell渲染的速度必須快。如何提高cell的渲染速度呢?

(1)當(dāng)有圖像時(shí),預(yù)渲染圖像,在bitmap context先將其畫一遍,導(dǎo)出成UIImage對(duì)象,然后再繪制到屏幕,這會(huì)大大提高渲染速度。具體內(nèi)容可以自行查找“利用預(yù)渲染加速顯示iOS圖像”相關(guān)資料。

(2)渲染最好時(shí)的操作之一就是混合(blending)了,所以我們不要使用透明背景,將cell的opaque值設(shè)為Yes,背景色不要使用clearColor,盡量不要使用陰影漸變等

(3)由于混合操作是使用GPU來執(zhí)行,我們可以用CPU來渲染,這樣混合操作就不再執(zhí)行??梢栽赨IView的drawRect方法中自定義繪制。

4、減少視圖的數(shù)目

我們?cè)赾ell上添加系統(tǒng)控件的時(shí)候,實(shí)際上系統(tǒng)都會(huì)調(diào)用底層的接口進(jìn)行繪制,大量添加控件時(shí),會(huì)消耗很大的資源并且也會(huì)影響渲染的性能。當(dāng)使用默認(rèn)的UITableViewCell并且在它的ContentView上面添加控件時(shí)會(huì)相當(dāng)消耗性能。所以目前最佳的方法還是繼承UITableViewCell,并重寫drawRect方法。

5、減少多余的繪制操作

在實(shí)現(xiàn)drawRect方法的時(shí)候,它的參數(shù)rect就是我們需要繪制的區(qū)域,在rect范圍之外的區(qū)域我們不需要進(jìn)行繪制,否則會(huì)消耗相當(dāng)大的資源。

6、不要給cell動(dòng)態(tài)添加subView

在初始化cell的時(shí)候就將所有需要展示的添加完畢,然后根據(jù)需要來設(shè)置hide屬性顯示和隱藏。

7、異步化UI,不要阻塞主線程

我們時(shí)常會(huì)看到這樣一個(gè)現(xiàn)象,就是加載時(shí)整個(gè)頁(yè)面卡住不動(dòng),怎么點(diǎn)都沒用,仿佛死機(jī)了一般。原因是主線程被阻塞了。所以對(duì)于網(wǎng)路數(shù)據(jù)的請(qǐng)求或者圖片的加載,我們可以開啟多線程,將耗時(shí)操作放到子線程中進(jìn)行,異步化操作。這個(gè)或許每個(gè)iOS開發(fā)者都知道的知識(shí),不必多講。

8、滑動(dòng)時(shí)按需加載對(duì)應(yīng)的內(nèi)容

如果目標(biāo)行與當(dāng)前行相差超過指定行數(shù),只在目標(biāo)滾動(dòng)范圍的前后指定3行加載。


-(void)scrollViewWillEndDragging:(UIScrollView *)scrollViewwithVelocity:(CGPoint)velocitytargetContentOffset:(inoutCGPoint *)targetContentOffset{


NSIndexPath *ip=[selfindexPathForRowAtPoint:CGPointMake(0,targetContentOffset->y)];


NSIndexPath *cip=[[selfindexPathsForVisibleRows]firstObject];


NSIntegerskipCount=8;


if(labs(cip.row-ip.row)>skipCount){


NSArray *temp=[selfindexPathsForRowsInRect:CGRectMake(0,targetContentOffset->y,self.width,self.height)];


NSMutableArray *arr=[NSMutableArrayarrayWithArray:temp];


if(velocity.y<0){


NSIndexPath *indexPath=[templastObject];


if(indexPath.row+33){


[arraddObject:[NSIndexPathindexPathForRow:indexPath.row-3inSection:0]];


[arraddObject:[NSIndexPathindexPathForRow:indexPath.row-2inSection:0]];


[arraddObject:[NSIndexPathindexPathForRow:indexPath.row-1inSection:0]];


}


}


[needLoadArraddObjectsFromArray:arr];


}


}


記得在tableView:cellForRowAtIndexPath:方法中加入判斷:


1

2

3

4

if(needLoadArr.count>0&&[needLoadArrindexOfObject:indexPath]==NSNotFound){

????[cellclear];

????return;

}

滑動(dòng)很快時(shí),只加載目標(biāo)范圍內(nèi)的cell,這樣按需加載(配合SDWebImage),極大提高流暢度。


14?優(yōu)化方案

官方對(duì)離屏渲染產(chǎn)生性能問題也進(jìn)行了優(yōu)化:

iOS 9.0之前UIimageView跟UIButton設(shè)置圓角都會(huì)觸發(fā)離屏渲染。

iOS 9.0之后UIButton設(shè)置圓角會(huì)觸發(fā)離屏渲染,而UIImageView里png圖片設(shè)置圓角不會(huì)觸發(fā)離屏渲染了,如果設(shè)置其他陰影效果之類的還是會(huì)觸發(fā)離屏渲染的。

(1)圓角優(yōu)化

在APP開發(fā)中,圓角圖片還是經(jīng)常出現(xiàn)的。如果一個(gè)界面中只有少量圓角圖片或許對(duì)性能沒有非常大的影響,但是當(dāng)圓角圖片比較多的時(shí)候就會(huì)APP性能產(chǎn)生明顯的影響。

我們?cè)O(shè)置圓角一般通過如下方式:

?imageView.layer.cornerRadius=CGFloat(10);


imageView.layer.masksToBounds=YES;

這樣處理的渲染機(jī)制是GPU在當(dāng)前屏幕緩沖區(qū)外新開辟一個(gè)渲染緩沖區(qū)進(jìn)行工作,也就是離屏渲染,這會(huì)給我們帶來額外的性能損耗,如果這樣的圓角操作達(dá)到一定數(shù)量,會(huì)觸發(fā)緩沖區(qū)的頻繁合并和上下文的的頻繁切換,性能的代價(jià)會(huì)宏觀地表現(xiàn)在用戶體驗(yàn)上——掉幀。

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


UIImageView *imageView?=?[[UIImageView?alloc]initWithFrame:CGRectMake(100,?100,?100,?100)];

imageView.image?=?[UIImage?imageNamed:@"myImg"];

//開始對(duì)imageView進(jìn)行畫圖?

UIGraphicsBeginImageContextWithOptions(imageView.bounds.size,NO,?1.0);

//使用貝塞爾曲線畫出一個(gè)圓形圖?

[[UIBezierPath?bezierPathWithRoundedRect:imageView.bounds?cornerRadius:imageView.frame.size.width]?addClip];

[imageView?drawRect:imageView.bounds];

imageView.image?=?UIGraphicsGetImageFromCurrentImageContext();

//結(jié)束畫圖?

UIGraphicsEndImageContext();

[self.view?addSubview:imageView];

優(yōu)化方案2:使用CAShapeLayer和UIBezierPath設(shè)置圓角

UIImageView *imageView=[[UIImageViewalloc]initWithFrame:CGRectMake(100,100,100,100)];


imageView.image=[UIImageimageNamed:@"myImg"];


UIBezierPath *maskPath=[UIBezierPathbezierPathWithRoundedRect:imageView.boundsbyRoundingCorners:UIRectCornerAllCornerscornerRadii:imageView.bounds.size];


CAShapeLayer *maskLayer=[[CAShapeLayeralloc]init];


//設(shè)置大小


maskLayer.frame=imageView.bounds;


//設(shè)置圖形樣子


maskLayer.path=maskPath.CGPath;


imageView.layer.mask=maskLayer;


[self.viewaddSubview:imageView];

對(duì)于方案2需要解釋的是:

??CAShapeLayer繼承于CALayer,可以使用CALayer的所有屬性值;

??CAShapeLayer需要貝塞爾曲線配合使用才有意義(也就是說才有效果)

??使用CAShapeLayer(屬于CoreAnimation)與貝塞爾曲線可以實(shí)現(xiàn)不在view的drawRect(繼承于CoreGraphics走的是CPU,消耗的性能較大)方法中畫出一些想要的圖形

??CAShapeLayer動(dòng)畫渲染直接提交到手機(jī)的GPU當(dāng)中,相較于view的drawRect方法使用CPU渲染而言,其效率極高,能大大優(yōu)化內(nèi)存使用情況。

總的來說就是用CAShapeLayer的內(nèi)存消耗少,渲染速度快,建議使用優(yōu)化方案2。

(2)shadow優(yōu)化

對(duì)于shadow,如果圖層是個(gè)簡(jiǎn)單的幾何圖形或者圓角圖形,我們可以通過設(shè)置shadowPath來優(yōu)化性能,能大幅提高性能。示例如下:

imageView.layer.shadowColor=[UIColorgrayColor].CGColor;


imageView.layer.shadowOpacity=1.0;


imageView.layer.shadowRadius=2.0;


UIBezierPath *path=[UIBezierPathbezierPathWithRect:imageView.frame];


imageView.layer.shadowPath=path.CGPath;


我們還可以通過設(shè)置shouldRasterize屬性值為YES來強(qiáng)制開啟離屏渲染。其實(shí)就是光柵化(Rasterization)。既然離屏渲染這么不好,為什么我們還要強(qiáng)制開啟呢?當(dāng)一個(gè)圖像混合了多個(gè)圖層,每次移動(dòng)時(shí),每一幀都要重新合成這些圖層,十分消耗性能。當(dāng)我們開啟光柵化后,會(huì)在首次產(chǎn)生一個(gè)位圖緩存,當(dāng)再次使用時(shí)候就會(huì)復(fù)用這個(gè)緩存。但是如果圖層發(fā)生改變的時(shí)候就會(huì)重新產(chǎn)生位圖緩存。所以這個(gè)功能一般不能用于UITableViewCell中,cell的復(fù)用反而降低了性能。最好用于圖層較多的靜態(tài)內(nèi)容的圖形。而且產(chǎn)生的位圖緩存的大小是有限制的,一般是2.5個(gè)屏幕尺寸。在100ms之內(nèi)不使用這個(gè)緩存,緩存也會(huì)被刪除。所以我們要根據(jù)使用場(chǎng)景而定。

(3)其他的一些優(yōu)化建議

??當(dāng)我們需要圓角效果時(shí),可以使用一張中間透明圖片蒙上去

??使用ShadowPath指定layer陰影效果路徑

??使用異步進(jìn)行l(wèi)ayer渲染(Facebook開源的異步繪制框架AsyncDisplayKit)

??設(shè)置layer的opaque值為YES,減少?gòu)?fù)雜圖層合成

??盡量使用不包含透明(alpha)通道的圖片資源

??盡量設(shè)置layer的大小值為整形值

??直接讓美工把圖片切成圓角進(jìn)行顯示,這是效率最高的一種方案

??很多情況下用戶上傳圖片進(jìn)行顯示,可以讓服務(wù)端處理圓角

??使用代碼手動(dòng)生成圓角Image設(shè)置到要顯示的View上,利用UIBezierPath(CoreGraphics框架)畫出來圓角圖片

(4)Core Animation工具檢測(cè)離屏渲染

對(duì)于離屏渲染的檢測(cè),蘋果為我們提供了一個(gè)測(cè)試工具Core Animation。可以在Xcode->Open Develeper Tools->Instruments中找到,如下圖:

Core Animation工具用來監(jiān)測(cè)Core Animation性能,提供可見的FPS值,并且提供幾個(gè)選項(xiàng)來測(cè)量渲染性能。如下圖:

下面我們來說明每個(gè)選項(xiàng)的功能:

Color Blended Layers:這個(gè)選項(xiàng)如果勾選,你能看到哪個(gè)layer是透明的,GPU正在做混合計(jì)算。顯示紅色的就是透明的,綠色就是不透明的。

Color Hits Green and Misses Red:如果勾選這個(gè)選項(xiàng),且當(dāng)我們代碼中有設(shè)置shouldRasterize為YES,那么紅色代表沒有復(fù)用離屏渲染的緩存,綠色則表示復(fù)用了緩存。我們當(dāng)然希望能夠復(fù)用。

Color Copied Images:按照官方的說法,當(dāng)圖片的顏色格式GPU不支持的時(shí)候,Core Animation會(huì)

拷貝一份數(shù)據(jù)讓CPU進(jìn)行轉(zhuǎn)化。例如從網(wǎng)絡(luò)上下載了TIFF格式的圖片,則需要CPU進(jìn)行轉(zhuǎn)化,這個(gè)區(qū)域會(huì)顯示成藍(lán)色。還有一種情況會(huì)觸發(fā)Core Animation的copy方法,就是字節(jié)不對(duì)齊的時(shí)候。如下圖:

Color Immediately:默認(rèn)情況下Core Animation工具以每毫秒10次的頻率更新圖層調(diào)試顏色,如果勾選這個(gè)選項(xiàng)則移除10ms的延遲。對(duì)某些情況需要這樣,但是有可能影響正常幀數(shù)的測(cè)試。

Color Misaligned Images:勾選此項(xiàng),如果圖片需要縮放則標(biāo)記為黃色,如果沒有像素對(duì)齊則標(biāo)記為紫色。像素對(duì)齊我們已經(jīng)在上面有所介紹。

Color Offscreen-Rendered Yellow:用來檢測(cè)離屏渲染的,如果顯示黃色,表示有離屏渲染。當(dāng)然還要結(jié)合Color Hits Green and Misses Red來看,是否復(fù)用了緩存。

Color OpenGL Fast Path Blue:這個(gè)選項(xiàng)對(duì)那些使用OpenGL的圖層才有用,像是GLKView或者CAEAGLLayer,如果不顯示藍(lán)色則表示使用了CPU渲染,繪制在了屏幕外,顯示藍(lán)色表示正常。

Flash Updated Regions:當(dāng)對(duì)圖層重繪的時(shí)候回顯示黃色,如果頻繁發(fā)生則會(huì)影響性能??梢杂迷黾泳彺鎭碓鰪?qiáng)性能。

以上就是本人的一些總結(jié),當(dāng)然對(duì)于UITableView的性能優(yōu)化,網(wǎng)上有很多相關(guān)的資料。如果有什么不同的觀點(diǎn),歡迎大家補(bǔ)充。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容