iOS面試題手記(2)

1.一個objc對象的isa的指針指向什么?有什么作用?

指向他的類對象,從而可以找到對象上的方法

2.一個 NSObject 對象占用多少內(nèi)存空間?

*一個NSObject對象占用的大小其實就是一個isa指針的大小。在64bit是8字節(jié)。 但是??!系統(tǒng)* *真正分配內(nèi)存的時候是分配了16字節(jié)!*

3.說一下對 class_rw_t 的理解?

rw代表可讀可寫,class_rw_t 中保存了Objc類的屬性,方法,遵循的協(xié)議等信息

// 可讀可寫

struct class_rw_t {

// Be warned that Symbolication knows the layout of this structure.

uint32_t flags;

uint32_t version;

const class_ro_t *ro; // 指向只讀的結(jié)構(gòu)體,存放類初始信息

/*

這三個都是二位數(shù)組,是可讀可寫的,包含了類的初始內(nèi)容、分類的內(nèi)容。

methods中,存儲 method_list_t ----> method_t

二維數(shù)組,method_list_t --> method_t

這三個二位數(shù)組中的數(shù)據(jù)有一部分是從class_ro_t中合并過來的。

*/

method_array_t methods; // 方法列表(類對象存放對象方法,元類對象存放類方法)

property_array_t properties; // 屬性列表

protocol_array_t protocols; //協(xié)議列表

Class firstSubclass;

Class nextSiblingClass;

//...

}

3.說一下對 class_ro_t 的理解?

存儲了當(dāng)前類在編譯器就已經(jīng)確定的屬性,方法以及遵循的協(xié)議

struct class_ro_t {

    uint32_t flags;

    uint32_t instanceStart;

    uint32_t instanceSize;

    uint32_t reserved;

    const uint8_t * ivarLayout;

    const char * name;

    method_list_t * baseMethodList;

    protocol_list_t * baseProtocols;

    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;

    property_list_t *baseProperties;

};

4.說一下對 isa 指針的理解, 對象的isa 指針指向哪里?isa 指針有哪兩種類型?

isa等價于is kind of,*實例對象isa指向類對象,類對象isa指向元類對象,元類對象isa指向元類的基類。

isa有兩種,1*純指針,指向內(nèi)存地址 2**NON_POINTER_ISA,除了內(nèi)存地址,還存有一些其他信息

5.實例對象的數(shù)據(jù)結(jié)構(gòu)?

本質(zhì)上objc_object的私有屬性只有一個isa指針,指向類對象的內(nèi)存地址

6.什么是method swizzling(俗稱黑魔法)

簡單來說就是方法交換,每個類都有一個方法列表,存著方法名字和實現(xiàn)的映射關(guān)系,selector的本質(zhì)其實就是方法名,IMP有點類似函數(shù)指針,指向具體的Method實現(xiàn),通過selector就可以找到對應(yīng)的IMP。換方法的幾種方式利用 method_exchangeImplementations 交換兩個方法的實現(xiàn),利用 class_replaceMethod替換方法的實現(xiàn),利用 method_setImplementation 來直接設(shè)置某個方法的IMP

7.什么時候會報unrecognized selector的異常?

objc在向一個對象發(fā)送消息時,runtime庫會根據(jù)isa指針找到該對象實際所屬的類,然后再該類的方法列表及父類方法列表中尋找方法運行,如果在最頂層父類依然找不到對應(yīng)方法,會進(jìn)入消息轉(zhuǎn)發(fā)階段,如果消息三次轉(zhuǎn)發(fā)流程任然未實現(xiàn),程序會拋出這個異常。

8.如何給 Category 添加屬性?關(guān)聯(lián)對象以什么形式進(jìn)行存儲?

關(guān)聯(lián)對象以哈希表的格式,存儲在一個全局的單例中

9.能否向編譯后得到的類中增加實例變量?能否向運行時創(chuàng)建的類中添加實例變量?為什么?

不能向編譯后得到的類中添加實例變量,能向運行時創(chuàng)建的類中添加實例變量

1.因為編譯后的類已經(jīng)注冊在runtime中,類結(jié)構(gòu)體中的實例變量離岸邊和內(nèi)存大小已確定,同時runtime會調(diào)用class_setvarlayout 或 class_setWeaklvarLayout 來處理strong weak 引用,所以不能添加

2.運行時創(chuàng)建的類是可以添加實例變量,調(diào)用class_addIvar函數(shù). 但是的在調(diào)用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上.

10.類對象的數(shù)據(jù)結(jié)構(gòu)?

類對象就是objc_class,是一個繼承自objc_object結(jié)構(gòu)體,所以包含isa指針

isa:指向元類

superClass:指向父類

Cache:方法的緩存列表

data:數(shù)據(jù),是一個被封裝好的class_rw_t

structobjc_class:objc_object{// Class ISA;Class superclass;//父類指針cache_t cache;// formerly cache pointer and vtable 方法緩存class_data_bits_t bits;// class_rw_t * plus custom rr/alloc flags 用于獲取地址class_rw_t*data(){returnbits.data();// &FAST_DATA_MASK 獲取地址值}

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

每個類對象都有一個方法列表,方法列表中記錄著方法的名稱,實現(xiàn)一鍵參數(shù)類型,通過方法名稱就可以在列表中找出對應(yīng)的方法實現(xiàn)

12.objc中向一個nil對象發(fā)送消息將會發(fā)生什么?

向一個nil對象發(fā)送消息,在尋找對象的isa指針的時候就0地址返回了,不會出現(xiàn)任何錯誤

詳解:

如果一個方法返回值是一個對象,那么發(fā)送給nil的消息將返回0(nil);

如果方法返回值為指針類型,其指針大小為小于或者等于sizeof(void*) ,float,double,long double 或者long long的整型標(biāo)量,發(fā)送給nil的消息將返回0;

如果方法返回值為結(jié)構(gòu)體,發(fā)送給nil的消息將返回0。結(jié)構(gòu)體中各個字段的值將都是0;

如果方法的返回值不是上述提到的幾種情況,那么發(fā)送給nil的消息的返回值將是未定義的。

13.objc在向一個對象發(fā)送消息時,發(fā)生了什么?

runtime會根據(jù)對象的isa指針找到該對象所屬的類,然后再方法列表以及父類方法列表中尋找方法實現(xiàn),如果一直到根類還沒找到,轉(zhuǎn)向攔截調(diào)用,走消息轉(zhuǎn)發(fā)機制,一旦找到,就去執(zhí)行他的IMP

14.isKindOfClass 與 isMemberOfClass

isKindOfClass 是確定一個對象是否是一個類的成員,或者是派生字該類

isMemberOfClass確定一個對象是否是當(dāng)前類的成員

15.Category 在編譯過后,是在什么時機與原有的類合并到一起的?

1.程序啟動后,通過編譯后,runtime初始化,調(diào)用_objc_init

2.然后會map_images

3.接下來調(diào)用map_images_nolock

4.然后read_images,讀取所有的類的相關(guān)信息

5.最后調(diào)用reMethodizeClass,進(jìn)行重新方法化

6.在reMethodizeClass內(nèi)部會調(diào)用attachCategorise,這個方法會傳入Class和Category,講方法和協(xié)議列表與原有的類合并,最后加入到class_rw_t結(jié)構(gòu)體中

16.Category 有哪些用途?

給系統(tǒng)類添加方法和屬性(需要關(guān)聯(lián)對象),對某個類大量方法,可以實現(xiàn)按照不同的名稱歸類

17.為什么 NSTimer 有時候不好使?

  因為創(chuàng)建的NSTimer默認(rèn)被加入到defaultMode,當(dāng)Runloop的Mode變化時,當(dāng)前timer不工作

18.RunLoop的Mode有幾種?
當(dāng)RunLoop在Mode1上時,是無法接受Mode2或Mode3上的Source,Timer,Observer事件的
總共有五種CFRunLoopMode:
KCFRunLoopDefaultMode:默認(rèn)模式,主線程就是在這個模式下運行
UITrackingRunLoopMode:跟蹤用戶交事件(用于ScrollView追蹤滑動)
UIInitiazationRunLoopMode:剛啟動App時進(jìn)入的第一個Mode,啟動完成后不再使用
GSEventReiviceRunLoopMode:接受系統(tǒng)內(nèi)部事件,通常用不到
KCFRunLoopCommonModes:偽模式,不是一種真正的運行模式,是同步Source/Timer?Observer到多個Mode的一種解決方案

19.RunLoop與NSTimer
一個比較常見的問題:滑動tableView時,定時器還會生效嗎?
默認(rèn)情況下RunLoop運行在kCFRunLoopDefaultMode下,而當(dāng)滑動tableView時,RunLoop切換到UITrackingRunLoopMode,而Timer是在kCFRunLoopDefaultMode下的,就無法接受處理Timer的事件。
怎么去解決這個問題呢?把Timer添加到UITrackingRunLoopMode上并不能解決問題,因為這樣在默認(rèn)情況下就無法接受定時器事件了。
所以我們需要把Timer同時添加到UITrackingRunLoopMode和kCFRunLoopDefaultMode上。
那么如何把timer同時添加到多個mode上呢?就要用到NSRunLoopCommonModes了

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

Timer就被添加到多個mode上,這樣即使RunLoop由kCFRunLoopDefaultMode切換到UITrackingRunLoopMode下,也不會影響接收Timer事件

20.AFNetworking 中如何運用 Runloop?

AFURLConnectionOperation 這個類是基于 NSURLConnection 構(gòu)建的,其希望能在后臺線程接收 Delegate 回調(diào)。為此 AFNetworking 單獨創(chuàng)建了一個線程,并在這個線程中啟動了一個 RunLoop.
RunLoop 啟動前內(nèi)部必須要有至少一個 Timer/Observer/Source,所以 AFNetworking 在 [runLoop run] 之前先創(chuàng)建了一個新的 NSMachPort 添加進(jìn)去了。通常情況下,調(diào)用者需要持有這個 NSMachPort (mach_port) 并在外部線程通過這個 port 發(fā)送消息到 loop 內(nèi);但此處添加 port 只是為了讓 RunLoop 不至于退出,并沒有用于實際的發(fā)送消息。

21.PerformSelector 的實現(xiàn)原理?
當(dāng)調(diào)用 NSObject 的 performSelecter:afterDelay: 或performSelector:onThread:后,實際內(nèi)部會創(chuàng)建一個Timer并添加到當(dāng)前線程的RunLoop中,如果當(dāng)前線程沒有RunLoop,方法會失效。
22.PerformSelector:afterDelay:這個方法在子線程中是否起作用?為什么?怎么解決?
不起作用,子線程默認(rèn)沒有RunLoop,也就沒有Timer,辦法是使用GCD來實現(xiàn):
Dispath_after

23.RunLoop和線程
線程和RunLoop是意義對應(yīng)的,隱射關(guān)系保存在一個全局字典中
自己創(chuàng)建的線程默認(rèn)是不開啟RunLoop的
1、怎么創(chuàng)建一個常駐線程?
1.為當(dāng)前線程開啟一個RunLoop(第一次調(diào)用 [NSRunLoop currentRunLoop]方法時實際是會先去創(chuàng)建一個RunLoop)
2.向RunLoop中添加一個維持RunLoop的時間循環(huán)(如果RunLoop的mode中一個item都沒有,RunLoop會退出)
3.啟動RunLoop

  @autoreleasepool {
        
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        
        [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        
        [runLoop run];
        
    }

2.怎樣保證子線程數(shù)據(jù)回來更新UI的時候不打斷用戶的滑動操作?
我們就可以將更新UI事件放在主線程的NSDefaultRunLoopMode上執(zhí)行即可,這樣就會等用戶不再滑動頁面,主線程RunLoop由UITrackingRunLoopMode切換到NSDefaultRunLoopMode時再去更新UI

[self performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO modes:@[NSDefaultRunLoopMode]];

24.RunLoop的數(shù)據(jù)結(jié)構(gòu)
RunLoop設(shè)計五個類:
CFRunLoop:RunLoop對象
CFRunLoopMode:運行模式
CFRunLoopSource:輸入源/事件源
CFRunLoopTimer:定時源
CFRunLoopObserver:觀察者
1、CFRunLoop

由pthread(線程對象,說明RunLoop和線程是一一對應(yīng)的)、currentMode(當(dāng)前所處的運行模式)、modes(多個運行模式的集合)、commonModes(模式名稱字符串集合)、commonModelItems(Observer,Timer,Source集合)構(gòu)成

2、CFRunLoopMode

由name、source0、source1、observers、timers構(gòu)成

3、CFRunLoopSource

分為source0和source1兩種

source0:
即非基于port的,也就是用戶觸發(fā)的事件。需要手動喚醒線程,將當(dāng)前線程從內(nèi)核態(tài)切換到用戶態(tài)
source1:
基于port的,包含一個 mach_port 和一個回調(diào),可監(jiān)聽系統(tǒng)端口和通過內(nèi)核和其他線程發(fā)送的消息,能主動喚醒RunLoop,接收分發(fā)系統(tǒng)事件。
具備喚醒線程的能力
4、CFRunLoopTimer

基于時間的觸發(fā)器,基本上說的就是NSTimer。在預(yù)設(shè)的時間點喚醒RunLoop執(zhí)行回調(diào)。因為它是基于RunLoop的,因此它不是實時的(就是NSTimer 是不準(zhǔn)確的。 因為RunLoop只負(fù)責(zé)分發(fā)源的消息。如果線程當(dāng)前正在處理繁重的任務(wù),就有可能導(dǎo)致Timer本次延時,或者少執(zhí)行一次)。

5、CFRunLoopObserver

監(jiān)聽以下時間點:CFRunLoopActivity

kCFRunLoopEntry
RunLoop準(zhǔn)備啟動

kCFRunLoopBeforeTimers
RunLoop將要處理一些Timer相關(guān)事件

kCFRunLoopBeforeSources
RunLoop將要處理一些Source事件

kCFRunLoopBeforeWaiting
RunLoop將要進(jìn)行休眠狀態(tài),即將由用戶態(tài)切換到內(nèi)核態(tài)

kCFRunLoopAfterWaiting
RunLoop被喚醒,即從內(nèi)核態(tài)切換到用戶態(tài)后

kCFRunLoopExit
RunLoop退出

kCFRunLoopAllActivities
監(jiān)聽所有狀態(tài)

25.RunLoop概念
RunLoop是通過維護(hù)內(nèi)部的時間循環(huán)來對事件/消息進(jìn)行管理的一個對象
1.沒有消息時,休眠避免占用資源,有用戶態(tài)切換到內(nèi)核態(tài)
2.有消息處理,由內(nèi)核態(tài)切換到用戶態(tài)
為什么main函數(shù)不會退出?
UIApplicationMain內(nèi)部默認(rèn)開啟了主線程的RunLoop,并執(zhí)行了一段無限循環(huán)的代 碼(不是簡單的for循環(huán)或while循環(huán))

26.解釋一下 NSTimer
NSTimer 其實就是 CFRunLoopTimerRef,他們之間是 toll-free bridged 的。一個 NSTimer 注冊到 RunLoop 后,RunLoop 會為其重復(fù)的時間點注冊好事件。例如 10:00, 10:10, 10:20 這幾個時間點。RunLoop 為了節(jié)省資源,并不會在非常準(zhǔn)確的時間點回調(diào)這個Timer。Timer 有個屬性叫做 Tolerance (寬容度),標(biāo)示了當(dāng)時間點到后,容許有多少最大誤差。

27.什么是異步繪制?
就是可以在子線程把需要繪制的圖形,提前在子線程處理好,講準(zhǔn)備好的圖像數(shù)據(jù)直接返回給主線程使用,降低主線程的壓力。
異步繪制過程:
通過系統(tǒng)的[view.delegate displayLayer:]入口實現(xiàn)異步繪制
*代理負(fù)責(zé)生成對應(yīng)的 Bitmap
*設(shè)置該 Bitmap 為 layer.contents 屬性的值。
28.利用 runloop 解釋一下頁面的渲染的過程?
當(dāng)我們調(diào)用 [UIView setNeedsDisplay] 時,這時會調(diào)用當(dāng)前 View.layer 的 [view.layer setNeedsDisplay]方法。

這等于給當(dāng)前的 layer 打上了一個臟標(biāo)記,而此時并沒有直接進(jìn)行繪制工作。而是會到當(dāng)前的 Runloop 即將休眠,也就是 beforeWaiting 時才會進(jìn)行繪制工作。

緊接著會調(diào)用 [CALayer display],進(jìn)入到真正繪制的工作。CALayer 層會判斷自己的 delegate 有沒有實現(xiàn)異步繪制的代理方法 displayer:,這個代理方法是異步繪制的入口,如果沒有實現(xiàn)這個方法,那么會繼續(xù)進(jìn)行系統(tǒng)繪制的流程,然后繪制結(jié)束。

CALayer 內(nèi)部會創(chuàng)建一個 Backing Store,用來獲取圖形上下文。接下來會判斷這個 layer 是否有 delegate。

如果有的話,會調(diào)用 [layer.delegate drawLayer:inContext:],并且會返回給我們 [UIView DrawRect:] 的回調(diào),讓我們在系統(tǒng)繪制的基礎(chǔ)之上再做一些事情。

如果沒有 delegate,那么會調(diào)用 [CALayer drawInContext:]。

以上兩個分支,最終 CALayer 都會將位圖提交到 Backing Store,最后提交給 GPU。

至此繪制的過程結(jié)束。

29.KVO (Key-value observing)
KVO就是觀察者模式的另一實現(xiàn),使用了isa混寫(isa-swizzling)來實現(xiàn)KVO
使用setter方法改變值KVO會生效,使用setValue:forKey即KVC改變值KVO也會生效,因為KVC會去調(diào)用setter方法
通過賦值成員變量不會觸發(fā)KVO,因為沒有調(diào)用setter方法,需要加上willChangeValueForKey和didChangeValueForKey方法來手動觸發(fā)才行

30.KVC(Key-value coding)

KVO可以允許開發(fā)者通過Key名直接訪問對象屬性或者給屬性賦值,而不需要調(diào)用存取方法。這樣就可以在運行時動態(tài)的訪問和修改對象屬性,而不是在編譯時確定。

31.分類、擴展、代理
一、分類
1.分類作用?
聲明私有方法,分解體積大的類文件,把framework的私有方法公開
2.分類特點
運行時決定,可以為系統(tǒng)類添加分類

32.請說一下對 CALayer 的認(rèn)識
layer是圖層繪制,渲染,以及動畫的完成者,無法處理觸摸事件,layer常見的屬性有Frame、Bounds、Position、AnchorPoint、Contents 等等。

33.Block的幾種形式
分為全局Block,堆Block,棧Block三種,其中棧Block存儲在棧(stack)區(qū),堆Block存儲在堆(heap)區(qū),全局Block存儲在已初始化數(shù)據(jù)(.data)區(qū)

34.什么是Block?
Block是將函數(shù)及其執(zhí)行上下文封裝起來的對象。block內(nèi)部有isa指針,所以說其本質(zhì)也是OC對象

35.iOS 性能優(yōu)化面試題
在性能優(yōu)化中一個最具參考價值的屬性是FPS:Frames Per Second,其實就是屏幕刷新率,蘋果的iphone推薦的刷新率是60Hz,F(xiàn)PS值的大小體現(xiàn)了頁面的流暢程度高低,當(dāng)?shù)陀?5的時候卡頓會比較明顯。
一.入門級
1.用ARC管理內(nèi)存
2、在正確的地方使用 reuseIdentifier
3.盡量把views設(shè)置成透明
4.避免過大的XIB
5.不要阻塞主線程
6.在ImageViews中調(diào)整圖片大小,最好保證圖片大小和imageView大小一致,縮放圖片會耗費資源,可以在下載完成后用backgroundthread,縮放一次,然后在UIImageView中使用縮放后的圖片。
7.正確使用Collection
8.打開gzip壓縮
二.中級
1.重用和延遲加載(lazy load) Views
2.Cache
緩存那些不大可能經(jīng)常改變但需要讀取的東西,一些選項是,遠(yuǎn)端服務(wù)器的響應(yīng),圖片,甚至計算結(jié)果,比如UITableView的行高。
3.權(quán)衡渲染方法.性能能還是要bundle保持合適的大小。
4.處理內(nèi)存警告,移除對緩存,圖片object和其他一些重創(chuàng)建的objects的strongreference
5.重用大開銷對象
6.一些objects的初始化很慢,比如NSDateFormatter和NSCalendar。然而,你又不可避免地需要使用它們,比如從JSON或者XML中解析數(shù)據(jù)。想要避免使用這個對象的瓶頸你就需要重用他們,可以通過添加屬性到你的class里或者創(chuàng)建靜態(tài)變量來實現(xiàn)。
7.避免反復(fù)處理數(shù)據(jù)庫,在服務(wù)端和客戶端使用相同的數(shù)據(jù)結(jié)構(gòu)
8.選擇正確的數(shù)據(jù)格式
9.正確設(shè)定背景圖片
10.減少使用Web特性,盡可能的移除不必要的js,避免使用過大的框架,盡可能的異步記載不影響頁面表達(dá)的js
11.、Shadow Path 。CoreAnimation不得不先在后臺得出你的圖形并加好陰影然后才渲染,這開銷是很大的。使用shadowPath的話就避免了這個問題。使用shadow path的話iOS就不必每次都計算如何渲染,它使用一個預(yù)先計算好的路徑。
12.優(yōu)化tableview
正確使用reuseIdentifier來重用cells
盡量使所有的view opaque,包括cell自身
避免漸變,圖片縮放,后臺選人
緩存行高
如果cell內(nèi)現(xiàn)實的內(nèi)容來自web,使用異步加載,緩存請求結(jié)果
使用shadowPath來畫陰影
減少subviews的數(shù)量
盡量不適用cellForRowAtIndexPath:,如果你需要用到它,只用-一次然后緩存結(jié)果
使用正確的數(shù)據(jù)結(jié)構(gòu)來存儲數(shù)據(jù)
使用rowHeight, sectionFooterHeight 和 sectionHeaderHeight來設(shè)定固定的高,不要請求delegate
13、選擇正確的數(shù)據(jù)存儲選項
三、高級
1.加速啟動時間,盡可能多的異步任務(wù),編碼龐大的XIB
2.使用Autorelease Pool
3.選擇是否緩存圖片
4.避免日期格式轉(zhuǎn)化
平時你是如何對代碼進(jìn)行性能優(yōu)化的?

利用性能分析工具檢測,包括靜態(tài) Analyze 工具,以及運行時 Profile 工具,通過Xcode工具欄中Product->Profile可以啟動,

比如測試程序啟動運行時間,當(dāng)點擊Time Profiler應(yīng)用程序開始運行后.就能獲取到整個應(yīng)用程序運行消耗時間分布和百分比.為了保證數(shù)據(jù)分析在統(tǒng)一使用場景真實需要注意一定要使用真機,因為此時模擬器是運行在Mac上,而Mac上的CPU往往比iOS設(shè)備要快。

為了防止一個應(yīng)用占用過多的系統(tǒng)資源,開發(fā)iOS的蘋果工程師門設(shè)計了一個“看門狗”的機制。在不同的場景下,“看門狗”會監(jiān)測應(yīng)用的性能。如果超出了該場景所規(guī)定的運行時間,“看門狗”就會強制終結(jié)這個應(yīng)用的進(jìn)程。開發(fā)者們在crashlog里面,會看到諸如0x8badf00d這樣的錯誤代碼。

36.光柵化
光柵化是將幾何數(shù)據(jù)經(jīng)過一系列變化后最終轉(zhuǎn)換成像素,從而呈現(xiàn)在顯示設(shè)備的過程,本質(zhì)是坐標(biāo)變化和幾何離散化。
當(dāng)UItableview和UICollectionView的cell樣式一樣是,可以使用這個屬性提高性能

cell.layer.shouldRasterize=YES;

cell.layer.rasterizationScale=[[UIScreenmainScreen]scale];


37.日常如何檢查內(nèi)存泄露?
目前我知道的方式有以下幾種:
Memory Leaks
Alloctions
Analyse
Debug Memory Graph
MLeaksFinder
泄露的內(nèi)存主要有一下兩種:
1.Laek Memory 忘記Relase操作所泄露的內(nèi)存
2.Abandon Memory 循環(huán)引用,無法釋放掉的內(nèi)存
38.如何高性能的畫一個圓角?

視圖和圓角的大小對幀率并沒有什么卵影響,數(shù)量才是傷害的核心輸出
label.layer.cornerRadius = 5
label.layer.masksToBounds = true

上面的方法不可取,會出發(fā)離屏渲染:
*如果能夠只用 cornerRadius 解決問題,就不用優(yōu)化。

*如果必須設(shè)置 masksToBounds,可以參考圓角視圖的數(shù)量,如果數(shù)量較少(一頁只有幾個)也可以考慮不用優(yōu)化。

*UIImageView的圓角可以通過直接截取圖片實現(xiàn)。其他視圖的圓角可以通過Core Graphics畫出圓角矩形實現(xiàn)

39.如何提升 tableview 的流暢度?
本質(zhì)上是降低 CPU、GPU 的工作,從這兩個大的方面去提升性能。

*CPU:對象的創(chuàng)建和銷毀、對象屬性的調(diào)整、布局計算、文本的計算和排版、圖片的格式轉(zhuǎn)換和解碼、圖像的繪制
*GPU:紋理的渲染
40.如何優(yōu)化 APP 的電量?
程序的耗電主要在以下四個方面:CPU,定位,網(wǎng)絡(luò),圖像
*盡可能降低CPU,GPU的功耗
*少用定時器
*優(yōu)化I/O操作:
*不要頻繁寫入小數(shù)據(jù),積攢到一定數(shù)量在寫入
*讀寫大量數(shù)據(jù)可使用 Dispatch_io ,GCD 內(nèi)部已經(jīng)做了優(yōu)化。
*數(shù)據(jù)量比較大,建議使用數(shù)據(jù)庫
*網(wǎng)絡(luò)方面的優(yōu)化
*減少壓縮網(wǎng)絡(luò)數(shù)據(jù)
*請求數(shù)據(jù)返回相同,使用NSCache緩存
*使用斷點續(xù)傳,避免因網(wǎng)絡(luò)失敗后重新下載
*網(wǎng)絡(luò)不可用時,不提供網(wǎng)絡(luò)請求
*長時間網(wǎng)絡(luò)請求,提供取消操作
*批量傳輸,下載視頻流的時候,盡量一大塊一大塊的進(jìn)行下載,廣告可以一次下
載多個
*定位層面的優(yōu)化
* 如果只是需要快速確定用戶位置,最好用 CLLocationManager 的 requestLocation 方法。定位完成后,會自動讓定位硬件斷電
*如果不是導(dǎo)航應(yīng)用,盡量不要實時更新位置,定位完畢就關(guān)掉定位服務(wù)
*盡量降低定位精度,比如盡量不要使用精度最高的 kCLLocationAccuracyBest
*需要后臺定位時,盡量設(shè)置 pausesLocationUpdatesAutomatically 為 YES,如果用戶不太可能移動的時候系統(tǒng)會自動暫停位置更新
*盡量不要使用 startMonitoringSignificantLocationChanges,優(yōu)先考慮 startMonitoringForRegion:
*硬件檢測優(yōu)化
*用戶移動、搖晃、傾斜設(shè)備時,會產(chǎn)生動作(motion)事件,這些事件由加速度計、陀螺儀、磁力計等硬件檢測。在不需要檢測的場合,應(yīng)該及時關(guān)閉這些硬件

41.如何有效降低 APP 包的大?。?br> 可執(zhí)行文件
*編譯器優(yōu)化
**Strip Linked Product、Make Strings Read-Only、Symbols Hidden by Default 設(shè)置為 YES
**去掉異常支持,Enable C++ Exceptions、Enable Objective-C Exceptions 設(shè)置為 NO, Other C Flags 添加 -fno-exceptions
*利用 AppCode 檢測未使用的代碼:菜單欄 -> Code -> Inspect Code
*編寫LLVM插件檢測出重復(fù)代碼、未被調(diào)用的代碼
資源
*資源包括 圖片、音頻、視頻 等

42.什么是 離屏渲染?什么情況下會觸發(fā)?該如何應(yīng)對?
離屏渲染就是在當(dāng)前屏幕緩沖區(qū)以外,新開辟一個緩沖器進(jìn)行操作
離屏渲染出發(fā)的場景有以下:
*圓角(maskToBounds并用才會觸發(fā))
*圖層蒙版
*陰影
*光柵化
為什么要避免離屏渲染?
CPU GPU 在繪制渲染視圖時做了大量的工作。離屏渲染發(fā)生在 GPU 層面上,會創(chuàng)建新的渲染緩沖區(qū),會觸發(fā) OpenGL 的多通道渲染管線,圖形上下文的切換會造成額外的開銷,增加 GPU 工作量。如果 CPU GPU 累計耗時 16.67 毫秒還沒有完成,就會造成卡頓掉幀。

43.NSThread+runloop實現(xiàn)常駐線程
*由于每次開辟子線程都會消耗cpu,在需要頻繁使用子線程的情況下,頻繁開辟子線程會消耗大量的cpu,而且創(chuàng)建線程都是任務(wù)執(zhí)行完成之后也就釋放了,不能再次利用,那么如何創(chuàng)建一個線程可以讓它可以再次工作呢?也就是創(chuàng)建一個常駐線程。

+ (NSThread *)shareThread {
    
    static NSThread *shareThread = nil;
    
    static dispatch_once_t oncePredicate;
    
    dispatch_once(&oncePredicate, ^{
        
        shareThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadTest2) object:nil];
        
        [shareThread setName:@"threadTest"];
        
        [shareThread start];
    });
    
    return shareThread;
}

+ (void)threadTest
{
    @autoreleasepool {
        
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        
        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        
        [runLoop run];
    }
}

44.自旋鎖與互斥鎖
自旋鎖會忙等: 所謂忙等,即在訪問被鎖資源時,調(diào)用者線程不會休眠,而是不停循環(huán)在那里,直到被鎖資源釋放鎖。
互斥鎖會休眠: 所謂休眠,即在訪問被鎖資源時,調(diào)用者線程會休眠,此時cpu可以調(diào)度其他線程工作。直到被鎖資源釋放鎖。此時會喚醒休眠線程。

45.內(nèi)存中的5大區(qū)分別是什么?
棧區(qū)(stack):由編譯器自動分配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等。其 操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。
堆區(qū)(heap):一般由程序員分配釋放, 若程序員不釋放,程序結(jié)束時可能由OS回收 。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事,分配方式倒是類似于鏈表。
全局區(qū)(靜態(tài)區(qū))(static):全局變量和靜態(tài)變量的存儲是放在一塊的,初始化的 全局變量和靜態(tài)變量在一塊區(qū)域, 未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域。 - 程序結(jié)束后由系統(tǒng)釋放。
文字常量區(qū):常量字符串就是放在這里的。 程序結(jié)束后由系統(tǒng)釋放。
程序代碼區(qū):存放函數(shù)體的二進(jìn)制代碼。

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

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

  • 1.為什么說Objective-C是一門動態(tài)的語言? 1.object-c類的類型和數(shù)據(jù)變量的類型都是在運行是確定...
    墨徠閱讀 955評論 0 0
  • UIKit 1.UIView 和 CALayer 是什么關(guān)系? UIView 繼承 UIResponder,而 U...
    Sephiroth_Ma閱讀 2,451評論 0 25
  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開了第一次的黨會,身份的轉(zhuǎn)變要...
    余生動聽閱讀 10,918評論 0 11
  • 彩排完,天已黑
    劉凱書法閱讀 4,500評論 1 3
  • 沒事就多看看書,因為腹有詩書氣自華,讀書萬卷始通神。沒事就多出去旅游,別因為沒錢而找借口,因為只要你省吃儉用,來...
    向陽之心閱讀 4,988評論 3 11

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