2020-02-16

1.iOS Runtime面試題(什么是method swizzling(俗稱黑魔法))

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

簡單說就是進(jìn)行方法交換

在Objective-C中調(diào)用一個方法,其實(shí)是向一個對象發(fā)送消息,查找消息的唯一依據(jù)是selector的名字。利用Objective-C的動態(tài)特性,可以實(shí)現(xiàn)在運(yùn)行時偷換selector對應(yīng)的方法實(shí)現(xiàn)垃你,達(dá)到給方法掛鉤的目的。

每個類都有一個方法列表庶弃,存放著方法的名字和方法實(shí)現(xiàn)的映射關(guān)系掌动,selector的本質(zhì)其實(shí)就是方法名,IMP有點(diǎn)類似函數(shù)指針记餐,指向具體的Method實(shí)現(xiàn)驮樊,通過selector就可以找到對應(yīng)的IMP。

換方法的幾種實(shí)現(xiàn)方式

*   利用 method_exchangeImplementations 交換兩個方法的實(shí)現(xiàn)
*   利用 class_replaceMethod替換方法的實(shí)現(xiàn)
*   利用 method_setImplementation 來直接設(shè)置某個方法的IMP

[圖片上傳失敗...(image-46db5a-1581791102546)]

2.iOS Runtime面試題(什么時候會報unrecognized selector的異常片酝?)

objc在向一個對象發(fā)送消息時囚衔,runtime庫會根據(jù)對象的isa指針找到該對象實(shí)際所屬的類,然后在該類中的方法列表以及其父類方法列表中尋找方法運(yùn)行雕沿,如果练湿,在最頂層的父類中依然找不到相應(yīng)的方法時,會進(jìn)入消息轉(zhuǎn)發(fā)階段晦炊,如果消息三次轉(zhuǎn)發(fā)流程仍未實(shí)現(xiàn)鞠鲜,則程序在運(yùn)行時會掛掉并拋出異常unrecognized selector sent to XXX

(1)runtime庫會根據(jù)對象的isa指針找到該對象實(shí)際所屬的類,然后在該類中的方法列表以及其父類方法列表中尋找方法運(yùn)行

(2)開始消息轉(zhuǎn)發(fā)

1.先征詢接收者能否動態(tài)添加方法(動態(tài)方法解析)

2.有沒有其他對象能夠處理

- (id)forwardingTargetForSelector:(SEL)aSelector{
 // 當(dāng)然這里可以通過判斷方法名來決定轉(zhuǎn)發(fā)的對象 
//NSString *seletorName = NSStringFromSelector(aSelector); 
//if([seletorName isEqualToString : @"xxx"]) 
  ClassB *b = [[ClassB alloc] init]; 
if([b respondsToSelector:aSelector]){
   return b;
 } 
return nil; }

3.運(yùn)行期系統(tǒng)把與消息有關(guān)的全部細(xì)節(jié)封裝到NSInvocation(完整消息轉(zhuǎn)發(fā))

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
 NSMethodSignature *signature = [super methodSignatureForSelector:aSelector]; // 當(dāng)然這里可以通過判斷方法名來決定轉(zhuǎn)發(fā)的對象
 //NSString *seletorName = NSStringFromSelector(aSelector); 
//if([seletorName isEqualToString : @"xxx"]) 
//ClassB *b = [[ClassB alloc] init];
 if(signature == nil){
   signature = [_b methodSignatureForSelector:aSelector]; 
}
 NSUInteger argCount = [signature numberOfArguments];
 for (NSInteger i=0; i<argCount; i++) { 
  NSLog(@"%s" , [signature getArgumentTypeAtIndex:i]);
 } 
NSLog(@"returnType:%s ,returnLen:%ld" , [signature methodReturnType] , [signature methodReturnLength]); NSLog(@"signature:%@" , signature); return signature; } 
- (void)forwardInvocation:(NSInvocation *)anInvocation{
 // 當(dāng)然這里可以通過判斷方法名來決定轉(zhuǎn)發(fā)的對象
 //NSString *seletorName = NSStringFromSelector(aSelector); 
//if([seletorName isEqualToString : @"xxx"]) SEL selector = [anInvocation selector]; 
//ClassB *b = [[ClassB alloc] init]; 
if([_b respondsToSelector:selector]){
 //這里如果沒有響應(yīng)断国,系統(tǒng)則會報錯崩潰 [anInvocation invokeWithTarget:_b];
 }
 }

4.如果完整消息轉(zhuǎn)發(fā)失敗則crash報unrecognized selector的異常

3.iOS Runtime面試題(如何給 Category 添加屬性贤姆?關(guān)聯(lián)對象以什么形式進(jìn)行存儲?)

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

查看的是 關(guān)聯(lián)對象 的知識點(diǎn)。

詳細(xì)的說一下 關(guān)聯(lián)對象薄疚。

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

找一下52個方法里的關(guān)聯(lián)key等知識=重病E椴辍!

@interface NSObject (Extension) @property (nonatomic,copy ) NSString *name; @end @implementation NSObject (Extension) - (void)setName:(NSString *)name { objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC); } - (NSString *)name { return objc_getAssociatedObject(self,@selector(name)); } @end

4.iOS Runtime面試題(能否向編譯后得到的類中增加實(shí)例變量板丽?能否向運(yùn)行時創(chuàng)建的類中添加實(shí)例變量呈枉?為什么?)

不能向編譯后得到的類中增加實(shí)例變量埃碱;

能向運(yùn)行時創(chuàng)建的類中添加實(shí)例變量猖辫;

1.因?yàn)榫幾g后的類已經(jīng)注冊在 runtime 中,類結(jié)構(gòu)體中的 objc_ivar_list 實(shí)例變量的鏈表和 instance_size 實(shí)例變量的內(nèi)存大小已經(jīng)確定,同時runtime會調(diào)用 class_setvarlayout 或 class_setWeaklvarLayout 來處理strong weak 引用.所以不能向存在的類中添加實(shí)例變量砚殿。

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

5.iOS Runtime面試題(類對象的數(shù)據(jù)結(jié)構(gòu)似炎?)

類對象就是 objc_class辛萍。

struct objc_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() { return bits.data(); // &FAST_DATA_MASK 獲取地址值 }

它的結(jié)構(gòu)相對豐富一些。繼承自objc_object結(jié)構(gòu)體羡藐,所以包含isa指針

*   isa:指向元類
*   superClass: 指向父類
*   Cache: 方法的緩存列表
*   data: 顧名思義叹阔,就是數(shù)據(jù)。是一個被封裝好的 class_rw_t传睹。

補(bǔ)充一下實(shí)例對象6薄!欧啤!

6.iOS Runtime面試題(runtime如何通過selector找到對應(yīng)的IMP地址睛藻?)

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

補(bǔ)充消息轉(zhuǎn)發(fā)時候的獲取方法列表等操作!P纤怼店印!

7.iOS Runtime面試題(runtime如何實(shí)現(xiàn)weak變量的自動置nil?知道SideTable嗎倒慧?)

runtime 對注冊的類會進(jìn)行布局按摘,對于weak修飾的對象會放到一個hash表中包券。用weak指向的對象內(nèi)存地址作為key,當(dāng)此對象的引用計(jì)數(shù)為0時會dealloc炫贤,加入weak指向的對象的內(nèi)存地址為a溅固,那么就會以a為鍵,那么就以a為鍵兰珍,在這個weak表中搜索侍郭,找到所有以a為鍵的weak對象,從而設(shè)置為nil

(1)初始化時runtime會調(diào)用objc_initWeak函數(shù)掠河,初始化一個新的weak指針指向?qū)ο蟮牡刂贰?
(2)添加引用時:objc_initWeak函數(shù)會調(diào)用objc_storeWeak函數(shù)objc_storeWeak()的作用是更新指針指向亮元,創(chuàng)建對應(yīng)的弱引用表。

(3)釋放時唠摹,調(diào)用clearDeallocating函數(shù)爆捞,clearDeallocating函數(shù)首先根據(jù)對象的地址獲取所有weak指針地址的數(shù)組,然后遍歷這個數(shù)組把其中的數(shù)據(jù)設(shè)置為nil勾拉,最后把這個entry從weak表中刪除嵌削,最后清理對象的記錄。

全部原理太多了在:[http://www.reibang.com/p/5de63ac9dab7](http://www.reibang.com/p/5de63ac9dab7)

總結(jié):

其實(shí)Weak表是一個hash(哈希)表望艺,Key是weak所指對象的地址苛秕,Value是weak指針的地址(這個地址的值是所指對象指針的地址)數(shù)組。

8.iOS Runtime面試題(objc中向一個nil對象發(fā)送消息將會發(fā)生什么找默?)

如果向一個nil對象發(fā)送消息艇劫,首先在尋找對象的isa指針時就是0地址返回了,所以不會出現(xiàn)任何錯誤惩激,也不會崩潰店煞。

詳解:

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

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

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

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

9.iOS Runtime面試題(objc在向一個對象發(fā)送消息時,發(fā)生了什么唆途?)

objc在向一個對象發(fā)送消息時富雅,runtime會根據(jù)對象的isa指針找到該對象實(shí)際所屬的類

然后在該類中的方法列表及其父類方法列表中尋找發(fā)發(fā)運(yùn)行掸驱,如果一直到根類還沒找到,轉(zhuǎn)向攔截調(diào)用没佑,走消息轉(zhuǎn)發(fā)機(jī)制毕贼,一旦找到,就去執(zhí)行他的IMP

10.iOS Runtime面試題(isKindOfClass 與 isMemberOfClass)

待補(bǔ)充M汲铩!让腹![http://www.reibang.com/p/87ce88d0bb99](http://www.reibang.com/p/87ce88d0bb99)

11.iOS Runtime面試題(Category 在編譯過后远剩,是在什么時機(jī)與原有的類合并到一起的?)

這里應(yīng)該去查一下object init的原理:稀9衔睢!

11.的原理也該查一下腹纳,下面寫的太繁瑣了

程序啟動后痢掠,通過編譯之后,Runtime 會進(jìn)行初始化嘲恍,調(diào)用 _objc_init足画。

然后會 map_images。

接下來調(diào)用 map_images_nolock佃牛。

再然后就是 read_images淹辞,這個方法會讀取所有的類的相關(guān)信息。

最后是調(diào)用 reMethodizeClass:俘侠,這個方法是重新方法化的意思象缀。

在 reMethodizeClass:方法內(nèi)部會調(diào)用 attachCategories:,這個方法會傳入 Class 和 Category 爷速,會將方法列表央星,協(xié)議列表等與原有的類合并。最后加入到 class_rw_t結(jié)構(gòu)體中惫东。

12.iOS Runtime面試題(Category 有哪些用途莉给?)

搜一下分類和擴(kuò)展區(qū)別實(shí)現(xiàn)!A凇禁谦!

還有分類為什么不能添加屬性(就是分類的結(jié)構(gòu)體里面關(guān)于屬性的問題)!7戏狻州泊!

13.iOS Runtime面試題(Category 的實(shí)現(xiàn)原理?)

被添加在了 class_rw_t 的對應(yīng)結(jié)構(gòu)里漂洋。

Category實(shí)際上是Category_t的結(jié)構(gòu)體遥皂,在運(yùn)行時力喷,新添加的方法,都被以倒敘插入到原有的方法列表的最前面演训,所以不同的Category弟孟,添加了同一個方法别惦,執(zhí)行的實(shí)際上是最后一個困肩。

下面代碼要更新下Q⒆选O崴狻实昨!

static struct _catrgory_t _OBJC_$_CATEGORY_NSObject_$_Tools __attribute__ ((used,section),("__DATA,__objc__const")) { // name // class // instance method list // class method list // protocol list // properties }

Category在剛剛編譯完的時候纳账,和原來的類是分開的叉寂,只有程序在運(yùn)行起來后赖瞒,通過Runtime震糖,Category和原來的類才會合并到一起

mememove录肯,memcpy,這兩個方法是位移吊说,復(fù)制论咏,簡單的理解就是原有的方法移動到最后,根據(jù)新開辟的控件颁井,把前面的位置留給分類厅贪,然后分類中的方法按照倒敘依次插入,可以得出的結(jié)論就是雅宾,越晚參與編譯的分類卦溢,里面的方法才是生效的那個

14.iOS Runtime面試題(_objc_msgForward函數(shù)是做什么的)

_objc_msgForward是 IMP 類型,用于消息轉(zhuǎn)發(fā)的:當(dāng)向一個對象發(fā)送一條消息秀又,但它并沒有實(shí)現(xiàn)的時候单寂,_objc_msgForward會嘗試做消息轉(zhuǎn)發(fā)。

詳解:_objc_msgForward在進(jìn)行消息轉(zhuǎn)發(fā)的過程中會涉及以下這幾個方法:

List itemresolveInstanceMethod:方法 (或resolveClassMethod:)吐辙。

List itemforwardingTargetForSelector:方法

List itemmethodSignatureForSelector:方法

List itemforwardInvocation:方法

List itemdoesNotRecognizeSelector: 方法

15.iOS Runtime面試題([self class] 與 [super class])

下面的代碼輸出什么宣决?

@implementation Son : Father - (id)init { self = [super init]; if (self) { NSLog(@"%@", NSStringFromClass([self class])); NSLog(@"%@", NSStringFromClass([super class])); } return self; } @end

NSStringFromClass([self class]) = Son

NSStringFromClass([super class]) = Son

詳解:這個題目主要是考察關(guān)于 Objective-C 中對 self 和 super 的理解。

self是類的隱藏參數(shù)昏苏,指向當(dāng)前調(diào)用方法的這個類的實(shí)例

super本質(zhì)是一個編譯器的標(biāo)識符尊沸,和self是指向一個消息接收者。不同點(diǎn)在于:super會告訴編譯器贤惯,當(dāng)調(diào)用方法是洼专,去調(diào)用父類的方法,而不是本類中的方法孵构。

當(dāng)使用self調(diào)用方法時屁商,則從父類的方法列表中開始找。然后調(diào)用父類的這個方法颈墅。

在調(diào)用[super class]的時候蜡镶,runtime會去調(diào)用 objc_msgSenderSuper方法雾袱,而不是objc_msgSend;

OBJC_EXPORT void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )

/// Specifies the superclass of an instance.

struct objc_super {

/// Specifies an instance of a class.

__unsafe_unretained id receiver;

/// Specifies the particular superclass of the instance to message.

#if !defined(__cplusplus) && !__OBJC2__

/* For compatibility with old objc-runtime.h header */

__unsafe_unretained Class class;

#else

__unsafe_unretained Class super_class;

#endif

/* super_class is the first class to search */

};

在objc_msgSendSuper方法中,第一個參數(shù)是一個objc_super的結(jié)構(gòu)體官还,這個結(jié)構(gòu)體里面有兩個變量芹橡,一個是當(dāng)前父類的super_class

objc_msgSenderSuper的工作原理應(yīng)該是這樣的

從objc_super結(jié)構(gòu)體指向的superClass父類的方法列表開始查找Selector,找到后以objc->receiver去調(diào)用父類的這個selector望伦。注意林说,最后調(diào)用者是objc_receiver,而不是super_class

那么objc_msgSendSuper最后就轉(zhuǎn)變成:

// 注意這里是從父類開始msgSend,而不是從本類開始 objc_msgSend(objc_super->receiver, @selector(class)) /// Specifies an instance of a class. 這是類的一個實(shí)例 __unsafe_unretained id receiver; // 由于是實(shí)例調(diào)用屯伞,所以是減號方法 - (Class)class { return object_getClass(self); }

由于找到了父類NSObject里面的class方法的IMP腿箩,又因?yàn)閭魅氲膮?shù)objc_super->receiver = self. self就是son,調(diào)用class愕掏,所以父類的方法class執(zhí)行IMP之后度秘,輸出的還是son最后輸出的兩個都一樣顶伞,都是輸出son

擴(kuò)展題:[http://www.reibang.com/p/a8e32de4858f](http://www.reibang.com/p/a8e32de4858f)

16.iOS Runloop面試題(為什么 NSTimer 有時候不好使饵撑?)

找一下!K裘病滑潘!

17.iOS Runloop面試題(PerformSelector 的實(shí)現(xiàn)原理?)

在寫多線程時候因?yàn)橹苯釉谥骱瘮?shù)里寫的多線程锨咙,但是多線程沒有開啟runloop(正確性待驗(yàn)證)

當(dāng)調(diào)用 NSObject 的 performSelecter:afterDelay: 后语卤,實(shí)際上其內(nèi)部會創(chuàng)建一個 Timer 并添加到當(dāng)前線程的 RunLoop 中。所以如果當(dāng)前線程沒有 RunLoop酪刀,則這個方法會失效粹舵。

當(dāng)調(diào)用 performSelector:onThread: 時,實(shí)際上其會創(chuàng)建一個 Timer 加到對應(yīng)的線程去骂倘,同樣的眼滤,如果對應(yīng)線程沒有 RunLoop 該方法也會失效。

18.iOS Runloop面試題(PerformSelector:afterDelay:這個方法在子線程中是否起作用历涝?為什么诅需?怎么解決?)

不起作用荧库,子線程默認(rèn)沒有 Runloop堰塌,也就沒有 Timer。

解決的辦法是可以使用 GCD 來實(shí)現(xiàn):Dispatch_after

19.iOS Runloop面試題(RunLoop的Mode)

關(guān)于Mode首先要知道一個RunLoop 對象中可能包含多個Mode分衫,且每次調(diào)用 RunLoop 的主函數(shù)時场刑,只能指定其中一個 Mode(CurrentMode)。切換 Mode蚪战,需要重新指定一個 Mode 摇邦。主要是為了分隔開不同的 Source恤煞、Timer、Observer施籍,讓它們之間互不影響居扒。

當(dāng)RunLoop運(yùn)行在Mode1上時,是無法接受處理Mode2或Mode3上的Source丑慎、Timer喜喂、Observer事件的

總共是有五種CFRunLoopMode:

*   kCFRunLoopDefaultMode:默認(rèn)模式,主線程是在這個運(yùn)行模式下運(yùn)行
*   UITrackingRunLoopMode:跟蹤用戶交互事件(用于 ScrollView 追蹤觸摸滑動竿裂,保證界面滑動時不受其他Mode影響)
*   UIInitializationRunLoopMode:在剛啟動App時第進(jìn)入的第一個 Mode玉吁,啟動完成后就不再使用
*   GSEventReceiveRunLoopMode:接受系統(tǒng)內(nèi)部事件,通常用不到
*   kCFRunLoopCommonModes:偽模式腻异,不是一種真正的運(yùn)行模式进副,是同步Source/Timer/Observer到多個Mode中的一種解決方案

20.iOS Runloop面試題(RunLoop的實(shí)現(xiàn)機(jī)制)

[http://www.reibang.com/p/cc1bb6cba76d](http://www.reibang.com/p/cc1bb6cba76d)

21.iOS Runloop面試題(RunLoop和線程)

*   線程和runloop是一一對應(yīng)的,其映射關(guān)系是保存在一個全局的Dictionary里
*   自己創(chuàng)建的線程默認(rèn)是沒有開啟RunLoop的

(1)怎么創(chuàng)建一個常駐線程悔常?

1.為當(dāng)前線程開啟了一個RunLoop(第一次調(diào)用[NSRunLoop currentRunLoop])方法時實(shí)際是會先創(chuàng)建一個 RunLoop

2.向當(dāng)前RunLoop中添加一個Port/Source等維持RunLoop的事件循環(huán)(如果RunLoop的mode中的一個item都沒有影斑,RunLoop會退出)

3.啟動該RunLoop

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

(2)輸出下邊代碼的執(zhí)行順序

NSLog(@"1"); dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"2"); [self performSelector:@selector(test) withObject:nil afterDelay:10]; NSLog(@"3"); }); NSLog(@"4"); - (void)test { NSLog(@"5"); }

答案是1423,test方法并不會執(zhí)行机打。

原因是如果是帶afterDelay的延時函數(shù)矫户,會在內(nèi)部創(chuàng)建一個 NSTimer,然后添加到當(dāng)前線程的RunLoop中残邀。也就是如果當(dāng)前線程沒有開啟RunLoop皆辽,該方法會失效。

那么我們改成:

dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"2"); [[NSRunLoop currentRunLoop] run]; [self performSelector:@selector(test) withObject:nil afterDelay:10]; NSLog(@"3"); });

然而test方法依然不執(zhí)行芥挣。

原因是如果RunLoop的mode中一個item都沒有,RunLoop會退出空免。即在調(diào)用RunLoop的run方法后空另,由于其mode中沒有添加任何item去維持RunLoop的時間循環(huán),RunLoop隨即還是會退出鼓蜒。

所以我們自己啟動RunLoop痹换,一定要在添加item后

dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"2"); [self performSelector:@selector(test) withObject:nil afterDelay:10]; [[NSRunLoop currentRunLoop] run];//一定是添加在item后,item維持RunLoop的時間循環(huán) NSLog(@"3"); });

(3)怎樣保證子線程數(shù)據(jù)回來更新UI的時候不打斷用戶的滑動操作都弹?

當(dāng)我們在子請求數(shù)據(jù)的同時滑動瀏覽當(dāng)前頁面娇豫,如果數(shù)據(jù)請求成功要切回主線程更新UI,那么就會影響當(dāng)前正在滑動的體驗(yàn)畅厢。

我們就可以將更新UI事件放在主線程的NSDefaultRunLoopMode上執(zhí)行即可冯痢,這樣就會等用戶不再滑動頁面,主線程RunLoop由UITrackingRunLoopMode切換到NSDefaultRunLoopMode時再去更新UI

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

22.iOS Runloop面試題(RunLoop的數(shù)據(jù)結(jié)構(gòu))

[http://www.reibang.com/p/64c478fe2cf1](http://www.reibang.com/p/64c478fe2cf1)

23.iOS Runloop面試題(RunLoop概念)

RunLoop是通過內(nèi)部維護(hù)的事件循環(huán)(Event Loop)來對事件/消息進(jìn)行管理的一個對象。

1浦楣、沒有消息處理時袖肥,休眠已避免資源占用,由用戶態(tài)切換到內(nèi)核態(tài)(CPU-內(nèi)核態(tài)和用戶態(tài))

2振劳、有消息需要處理時椎组,立刻被喚醒,由內(nèi)核態(tài)切換到用戶態(tài)

為什么main函數(shù)不會退出历恐?

int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } }

UIApplicationMain內(nèi)部默認(rèn)開啟了主線程的RunLoop寸癌,并執(zhí)行了一段無限循環(huán)的代碼(不是簡單的for循環(huán)或while循環(huán))

//無限循環(huán)代碼模式(偽代碼) int main(int argc, char * argv[]) { BOOL running = YES; do { // 執(zhí)行各種任務(wù),處理各種事件 // ...... } while (running); return 0; }

UIApplicationMain函數(shù)一直沒有返回弱贼,而是不斷地接收處理消息以及等待休眠蒸苇,所以運(yùn)行程序之后會保持持續(xù)運(yùn)行狀態(tài)。

24.iOS Runloop面試題(講一下 Observer 吮旅?)

[http://www.reibang.com/p/bac5c2462e69](http://www.reibang.com/p/bac5c2462e69)

25.iOS Runloop面試題(解釋一下 NSTimer溪烤。)

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

如果某個時間點(diǎn)被錯過了聂受,例如執(zhí)行了一個很長的任務(wù),則那個時間點(diǎn)的回調(diào)也會跳過去烤镐,不會延后執(zhí)行蛋济。就比如等公交,如果 10:10 時我忙著玩手機(jī)錯過了那個點(diǎn)的公交炮叶,那我只能等 10:20 這一趟了碗旅。

CADisplayLink是一個和屏幕刷新率一致的定時器(但實(shí)際實(shí)現(xiàn)原理更復(fù)雜,和 NSTimer 并不一樣镜悉,其內(nèi)部實(shí)際是操作了一個 Source)祟辟。如果在兩次屏幕刷新之間執(zhí)行了一個長任務(wù),那其中就會有一幀被跳過去(和 NSTimer相似)侣肄,造成界面卡頓的感覺旧困。在快速滑動 TableView時,即使一幀的卡頓也會讓用戶有所察覺。Facebook開源的 AsyncDisplayLink就是為了解決界面卡頓的問題吼具,其內(nèi)部也用到了 RunLoop僚纷。

26.iOS Runloop面試題(解釋一下 `事件響應(yīng)` 的過程?)

[http://www.reibang.com/p/9c3d136302be](http://www.reibang.com/p/9c3d136302be)

27.iOS Runloop面試題(解釋一下 手勢識別 的過程拗盒?)

[http://www.reibang.com/p/32d97298d669](http://www.reibang.com/p/32d97298d669)

順便補(bǔ)充下UIGestureRecognizer和touch事件2澜摺!陡蝇!

28.iOS Runloop面試題(什么是異步繪制侵状?)

異步繪制,就是可以在子線程把需要繪制的圖形毅整,提前在子線程處理好趣兄。將準(zhǔn)備好的圖像數(shù)據(jù)直接返給主線程使用,這樣可以降低主線程的壓力悼嫉。

異步繪制的過程

要通過系統(tǒng)的 [view.delegate displayLayer:]這個入口來實(shí)現(xiàn)異步繪制艇潭。

*   代理負(fù)責(zé)生成對應(yīng)的 Bitmap
*   設(shè)置該 Bitmap 為 layer.contents 屬性的值。

O访铩LD![http://www.reibang.com/p/6634dbdf2964](http://www.reibang.com/p/6634dbdf2964)

W芸谩w⒓拧![http://www.reibang.com/p/ee67b17dbf19](http://www.reibang.com/p/ee67b17dbf19)

29.iOS Runloop面試題(利用 runloop 解釋一下頁面的渲染的過程情龄?)

[http://www.reibang.com/p/8900516f1644](http://www.reibang.com/p/8900516f1644)

30.iOS OC底層面試題(KVC(Key-value coding))

-(id)valueForKey:(NSString *)key; -(void)setValue:(id)value forKey:(NSString *)key;

KVC就是指iOS的開發(fā)中迄汛,可以允許開發(fā)者通過Key名直接訪問對象的屬性,或者給對象的屬性賦值骤视。而不需要調(diào)用明確的存取方法鞍爱。這樣就可以在運(yùn)行時動態(tài)地訪問和修改對象的屬性。而不是在編譯時確定专酗,這也是iOS開發(fā)中的黑魔法之一睹逃。很多高級的iOS開發(fā)技巧都是基于KVC實(shí)現(xiàn)的

當(dāng)調(diào)用setValue:屬性值forKey:@”name“的代碼時,祷肯,底層的執(zhí)行機(jī)制如下:

*   程序優(yōu)先調(diào)用set<Key>:屬性值方法沉填,代碼通過setter方法完成設(shè)置。注意佑笋,這里的<key>是指成員變量名翼闹,首字母大小寫要符合KVC的命名規(guī)則,下同
*   如果沒有找到setName:方法允青,KVC機(jī)制會檢查+ (BOOL)accessInstanceVariablesDirectly方法有沒有返回YES橄碾,默認(rèn)該方法會返回YES卵沉,如果你重寫了該方法讓其返回NO的話,那么在這一步KVC會執(zhí)行setValue:forUndefinedKey:方法法牲,不過一般開發(fā)者不會這么做史汗。所以KVC機(jī)制會搜索該類里面有沒有名為_<key>的成員變量,無論該變量是在類接口處定義拒垃,還是在類實(shí)現(xiàn)處定義停撞,也無論用了什么樣的訪問修飾符,只在存在以<key>命名的變量悼瓮,KVC都可以對該成員變量賦值戈毒。
*   如果該類即沒有set<key>:方法,也沒有_<key>成員變量横堡,KVC機(jī)制會搜索_is<Key>的成員變量埋市。
*   和上面一樣,如果該類即沒有set<Key>:方法命贴,也沒有_<key>和_is<Key>成員變量道宅,KVC機(jī)制再會繼續(xù)搜索<key>和is<Key>的成員變量。再給它們賦值胸蛛。
*   如果上面列出的方法或者成員變量都不存在污茵,系統(tǒng)將會執(zhí)行該對象的setValue:forUndefinedKey:方法,默認(rèn)是拋出異常葬项。

即如果沒有找到Set<Key>方法的話泞当,會按照_key,_iskey民珍,key襟士,iskey的順序搜索成員并進(jìn)行賦值操作。

如果開發(fā)者想讓這個類禁用KVC穷缤,那么重寫+ (BOOL)accessInstanceVariablesDirectly方法讓其返回NO即可敌蜂,這樣的話如果KVC沒有找到set<Key>:屬性名時箩兽,會直接用setValue:forUndefinedKey:方法津肛。

當(dāng)調(diào)用valueForKey:@”name“的代碼時,KVC對key的搜索方式不同于setValue:屬性值 forKey:@”name“汗贫,其搜索方式如下:

*   首先按get<Key>,<key>,is<Key>的順序方法查找getter方法身坐,找到的話會直接調(diào)用。如果是BOOL或者Int等值類型落包, 會將其包裝成一個NSNumber對象部蛇。
*   如果上面的getter沒有找到,KVC則會查找countOf<Key>,objectIn<Key>AtIndex或<Key>AtIndexes格式的方法咐蝇。如果countOf<Key>方法和另外兩個方法中的一個被找到涯鲁,那么就會返回一個可以響應(yīng)NSArray所有方法的代理集合(它是NSKeyValueArray,是NSArray的子類),調(diào)用這個代理集合的方法抹腿,或者說給這個代理集合發(fā)送屬于NSArray的方法岛请,就會以countOf<Key>,objectIn<Key>AtIndex或<Key>AtIndexes這幾個方法組合的形式調(diào)用。還有一個可選的get<Key>:range:方法警绩。所以你想重新定義KVC的一些功能崇败,你可以添加這些方法,需要注意的是你的方法名要符合KVC的標(biāo)準(zhǔn)命名方法肩祥,包括方法簽名后室。
*   如果上面的方法沒有找到,那么會同時查找countOf<Key>混狠,enumeratorOf<Key>,memberOf<Key>格式的方法岸霹。如果這三個方法都找到,那么就返回一個可以響應(yīng)NSSet所的方法的代理集合将饺,和上面一樣松申,給這個代理集合發(fā)NSSet的消息,就會以countOf<Key>俯逾,enumeratorOf<Key>,memberOf<Key>組合的形式調(diào)用贸桶。
*   如果還沒有找到,再檢查類方法+

(BOOL)accessInstanceVariablesDirectly,如果返回YES(默認(rèn)行為)桌肴,那么和先前的設(shè)值一樣皇筛,會按_<key>,_is<Key>,<key>,is<Key>的順序搜索成員變量名,這里不推薦這么做坠七,因?yàn)檫@樣直接訪問實(shí)例變量破壞了封裝性水醋,使代碼更脆弱。如果重寫了類方法+ (BOOL)accessInstanceVariablesDirectly返回NO的話彪置,那么會直接調(diào)用valueForUndefinedKey:方法拄踪,默認(rèn)是拋出異常。

31.iOS OC底層面試題(KVO (Key-value observing))

待添加H;掏!

*   那么通過直接賦值成員變量會觸發(fā)KVO嗎潘懊?

不會姚糊,因?yàn)椴粫{(diào)用setter方法,需要加上

willChangeValueForKey和didChangeValueForKey方法來手動觸發(fā)才行

32.iOS OC底層面試題(分類授舟、擴(kuò)展救恨、代理(Delegate))

一、分類

*   1.分類的作用释树?

聲明私有方法肠槽,分解體積大的類文件擎淤,把framework的私有方法公開

*   2.分類的特點(diǎn)

運(yùn)行時決議,可以為系統(tǒng)類添加分類 秸仙。

說得詳細(xì)些揉燃,在運(yùn)行時時期,將 Category 中的實(shí)例方法列表筋栋、協(xié)議列表炊汤、屬性列表添加到主類中后(所以Category中的方法在方法列表中的位置是在主類的同名方法之前的),然后會遞歸調(diào)用所有類的 load 方法弊攘,這一切都是在main函數(shù)之前執(zhí)行的抢腐。

*   3.分類可以添加哪些內(nèi)容?

實(shí)例方法襟交,類方法迈倍,協(xié)議,屬性(添加getter和setter方法捣域,并沒有實(shí)例變量啼染,添加實(shí)例變量需要用關(guān)聯(lián)對象)

*   4.如果工程里有兩個分類A和B,兩個分類中有一個同名的方法焕梅,哪個方法最終生效迹鹅?

取決于分類的編譯順序,最后編譯的那個分類的同名方法最終生效贞言,而之前的都會被覆蓋掉(這里并不是真正的覆蓋斜棚,因?yàn)槠溆喾椒ㄈ匀淮嬖冢皇窃L問不到该窗,因?yàn)樵趧討B(tài)添加類的方法的時候是倒序遍歷方法列表的弟蚀,而最后編譯的分類的方法會放在方法列表前面,訪問的時候就會先被訪問到酗失,同理如果聲明了一個和原類方法同名的方法义钉,也會覆蓋掉原類的方法)。

*   5.如果聲明了兩個同名的分類會怎樣规肴?

會報錯捶闸,所以第三方的分類,一般都帶有命名前綴

*   6.分類能添加成員變量嗎奏纪?<汀!序调!

不能。只能通過關(guān)聯(lián)對象(objc_setAssociatedObject)來模擬實(shí)現(xiàn)成員變量兔簇,但其實(shí)質(zhì)是關(guān)聯(lián)內(nèi)容发绢,所有對象的關(guān)聯(lián)內(nèi)容都放在同一個全局容器哈希表中:AssociationsHashMap,由AssociationsManager統(tǒng)一管理硬耍。

二、擴(kuò)展

*   1.一般用擴(kuò)展做什么边酒?

聲明私有屬性经柴,聲明方法(沒什么意義),聲明私有成員變量

*   2.擴(kuò)展的特點(diǎn)

編譯時決議墩朦,只能以聲明的形式存在坯认,多數(shù)情況下寄生在宿主類的.m中,不能為系統(tǒng)類添加擴(kuò)展氓涣。

三牛哺、代理(Delegate)

代理是一種設(shè)計(jì)模式耀鸦,以@protocol形式體現(xiàn)瓶籽,一般是一對一傳遞。

一般以weak關(guān)鍵詞以規(guī)避循環(huán)引用宗弯。

待補(bǔ)充Q魍妗4靖健!

33.iOS OC底層面試題(屬性關(guān)鍵字copy)

可變對象的copy和mutableCopy都是深拷貝

不可變對象的copy是淺拷貝蠢古,mutableCopy是深拷貝E铩!草讶!

copy方法返回的都是不可變對象

*   @property (nonatomic, copy) NSMutableArray * array;這樣寫有什么影響缆毁?

因?yàn)閏opy方法返回的都是不可變對象,所以array對象實(shí)際上是不可變的到涂,如果對其進(jìn)行可變操作如添加移除對象脊框,則會造成程序crash!<摹浇雹!

assign:修飾基本數(shù)據(jù)類型,修飾對象類型時屿讽,不改變其引用計(jì)數(shù)昭灵,會產(chǎn)生懸垂指針,修飾的對象在被釋放后伐谈,assign指針仍然指向原對象內(nèi)存地址烂完,如果使用assign指針繼續(xù)訪問原對象的話,就可能會導(dǎo)致內(nèi)存泄漏或程序異常(實(shí)際上會報錯诵棵?抠蚣??)

34.iOS Animation面試題(請說一下對 CALayer 的認(rèn)識履澳。)

layer層是涂層繪制嘶窄、渲染怀跛、以及動畫的完成者,它無法直接的處理觸摸事件(也可以捕捉事件)

layer包含的方面非常多柄冲,常見的屬性有 Frame吻谋、Bounds、Position现横、AnchorPoint漓拾、Contents等等。

35.iOS Animation面試題(`CALayer` 的 `Contents` 有幾下幾個主要的屬性:)

待補(bǔ)充=潇簟:Я健!

36.iOS Block面試題(Block的幾種形式)

比較基礎(chǔ):[http://www.reibang.com/p/ab5cd4153bf8](http://www.reibang.com/p/ab5cd4153bf8)

37.iOS Block面試題(Block變量截獲)

待補(bǔ)充5枚摺8铡![http://www.reibang.com/p/dd5fea4dcea8](http://www.reibang.com/p/dd5fea4dcea8)

38.iOS Block面試題(什么是Block贩据?)

待補(bǔ)充6安佟!饱亮![http://www.reibang.com/p/cc91ff650d6c](http://www.reibang.com/p/cc91ff650d6c)

39.iOS UI相關(guān)面試題

一矾芙、UIView與CALayer

<單一職責(zé)原則>

UIView為CALayer提供內(nèi)容,以及負(fù)責(zé)處理觸摸等事件近上,參與響應(yīng)鏈

CALayer負(fù)責(zé)顯示內(nèi)容contents

二剔宪、事件傳遞與視圖響應(yīng)鏈 :

待補(bǔ)充!R嘉蕖葱绒!

三、圖像顯示原理

1.CPU:輸出位圖

2.GPU :圖層渲染斗锭,紋理合成

3.把結(jié)果放到幀緩沖區(qū)(frame buffer)中

4.再由視頻控制器根據(jù)vsync信號在指定時間之前去提取幀緩沖區(qū)的屏幕顯示內(nèi)容

5.顯示到屏幕上

CPU工作

1.Layout: UI布局地淀,文本計(jì)算

2.Display: 繪制

3.Prepare: 圖片解碼

4.Commit:提交位圖

GPU渲染管線(OpenGL)

頂點(diǎn)著色,圖元裝配岖是,光柵化帮毁,片段著色,片段處理

四豺撑、UI卡頓掉幀原因

iOS設(shè)備的硬件時鐘會發(fā)出Vsync(垂直同步信號)烈疚,然后App的CPU會去計(jì)算屏幕要顯示的內(nèi)容,之后將計(jì)算好的內(nèi)容提交到GPU去渲染聪轿。隨后爷肝,GPU將渲染結(jié)果提交到幀緩沖區(qū),等到下一個VSync到來時將緩沖區(qū)的幀顯示到屏幕上。也就是說阶剑,一幀的顯示是由CPU和GPU共同決定的跃巡。

一般來說危号,頁面滑動流暢是60fps牧愁,也就是1s有60幀更新,即每隔16.7ms就要產(chǎn)生一幀畫面外莲,而如果CPU和GPU加起來的處理時間超過了16.7ms猪半,就會造成掉幀甚至卡頓。

五偷线、滑動優(yōu)化方案

CPU:

把以下操作放在子線程中

1.對象創(chuàng)建磨确、調(diào)整、銷毀

2.預(yù)排版(布局計(jì)算声邦、文本計(jì)算乏奥、緩存高度等等)

3.預(yù)渲染(文本等異步繪制,圖片解碼等)

GPU:

紋理渲染亥曹,視圖混合

一般遇到性能問題時邓了,考慮以下問題:

是否受到CPU或者GPU的限制?

是否有不必要的CPU渲染媳瞪?

是否有太多的離屏渲染操作骗炉?

是否有太多的圖層混合操作?

是否有奇怪的圖片格式或者尺寸蛇受?

是否涉及到昂貴的view或者效果句葵?

view的層次結(jié)構(gòu)是否合理?

六兢仰、UI繪制原理

異步繪制:

[self.layer.delegate displayLayer: ]

代理負(fù)責(zé)生成對應(yīng)的bitmap

設(shè)置該bitmap作為該layer.contents屬性的值

七乍丈、離屏渲染

On-Screen Rendering:當(dāng)前屏幕渲染,指的是GPU的渲染操作是在當(dāng)前用于顯示的屏幕緩沖區(qū)中進(jìn)行

Off-Screen Rendering:離屏渲染把将,分為CPU離屏渲染和GPU離屏渲染兩種形式轻专。GPU離屏渲染指的是GPU在當(dāng)前屏幕緩沖區(qū)外新開辟一個緩沖區(qū)進(jìn)行渲染操作

應(yīng)當(dāng)盡量避免的則是GPU離屏渲染

GPU離屏渲染何時會觸發(fā)呢?

圓角(當(dāng)和maskToBounds一起使用時)秸弛、圖層蒙版铭若、陰影,設(shè)置

layer.shouldRasterize = YES

為什么要避免GPU離屏渲染递览?

GPU需要做額外的渲染操作叼屠。通常GPU在做渲染的時候是很快的,但是涉及到offscreen-render的時候情況就可能有些不同绞铃,因?yàn)樾枰~外開辟一個新的緩沖區(qū)進(jìn)行渲染镜雨,然后繪制到當(dāng)前屏幕的過程需要做onscreen跟offscreen上下文之間的切換,這個過程的消耗會比較昂貴儿捧,涉及到OpenGL的pipeline跟barrier荚坞,而且offscreen-render在每一幀都會涉及到挑宠,因此處理不當(dāng)肯定會對性能產(chǎn)生一定的影響。另外由于離屏渲染會增加GPU的工作量颓影,可能會導(dǎo)致CPU+GPU的處理時間超出16.7ms各淀,導(dǎo)致掉幀卡頓。所以可以的話應(yīng)盡量減少offscreen-render的圖層

40.iOS 多線程面試題(進(jìn)程诡挂、線程)

一碎浇、 進(jìn)程:

*   1.進(jìn)程是一個具有一定獨(dú)立功能的程序關(guān)于某次數(shù)據(jù)集合的一次運(yùn)行活動,它是操作系統(tǒng)分配資源的最小單元.
*   2.進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個應(yīng)用程序璃俗,就是一段程序的執(zhí)行過程,我們可以理解為手機(jī)上的一個app.
*   3.每個進(jìn)程之間是獨(dú)立的奴璃,每個進(jìn)程均運(yùn)行在其專用且受保護(hù)的內(nèi)存空間內(nèi),擁有獨(dú)立運(yùn)行所需的全部資源

補(bǔ)充進(jìn)程通信3腔怼9赌隆!唱星?雳旅??

二魏颓、 線程

*   1.程序執(zhí)行流的最小單元岭辣,線程是進(jìn)程中的一個實(shí)體.
*   2.一個進(jìn)程要想執(zhí)行任務(wù),必須至少有一條線程.應(yīng)用程序啟動的時候,系統(tǒng)會默認(rèn)開啟一條線程,也就是主線程

三甸饱、 進(jìn)程和線程的關(guān)系

*   1.線程是進(jìn)程的執(zhí)行單元沦童,進(jìn)程的所有任務(wù)都在線程中執(zhí)行
*   2.線程是 CPU 分配資源和調(diào)度的最小單位
*   3.一個程序可以對應(yīng)多個進(jìn)程(多進(jìn)程),一個進(jìn)程中可有多個線程,但至少要有一條線程
*   4.同一個進(jìn)程內(nèi)的線程共享進(jìn)程資源

41.iOS 多線程面試題(死鎖)

1、一個比較常見的死鎖例子:主隊(duì)列同步

- (void)viewDidLoad { [super viewDidLoad]; dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"deallock"); }); // Do any additional setup after loading the view, typically from a nib. }

在主線程中運(yùn)用主隊(duì)列同步叹话,也就是把任務(wù)放到了主線程的隊(duì)列中偷遗。

同步對于任務(wù)是立刻執(zhí)行的,那么當(dāng)把任務(wù)放進(jìn)主隊(duì)列時驼壶,它就會立馬執(zhí)行,只有執(zhí)行完這個任務(wù)氏豌,viewDidLoad才會繼續(xù)向下執(zhí)行。H劝肌1么!

而viewDidLoad和任務(wù)都是在主隊(duì)列上的般妙,由于隊(duì)列的先進(jìn)先出原則纪铺,任務(wù)又需等待viewDidLoad執(zhí)行完畢后才能繼續(xù)執(zhí)行,viewDidLoad和這個任務(wù)就形成了相互循環(huán)等待碟渺,就造成了死鎖鲜锚。!!芜繁!

想避免這種死鎖旺隙,可以將同步改成異步dispatch_async,或者將dispatch_get_main_queue換成其他串行或并行隊(duì)列,都可以解決骏令。J呓荨!伏社!

2抠刺、同樣塔淤,下邊的代碼也會造成死鎖:

dispatch_queue_t serialQueue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL); dispatch_async(serialQueue, ^{ dispatch_sync(serialQueue, ^{ NSLog(@"deadlock"); }); });

外面的函數(shù)無論是同步還是異步都會造成死鎖摘昌。!8叻洹聪黎!

這是因?yàn)槔锩娴娜蝿?wù)和外面的任務(wù)都在同一個serialQueue隊(duì)列內(nèi),又是同步备恤,這就和上邊主隊(duì)列同步的例子一樣造成了死鎖8迨巍!露泊!

解決方法也和上邊一樣喉镰,將里面的同步改成異步dispatch_async,或者將serialQueue換成其他串行或并行隊(duì)列,都可以解決

dispatch_queue_t serialQueue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL); dispatch_queue_t serialQueue2 = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL); dispatch_async(serialQueue, ^{ dispatch_sync(serialQueue2, ^{ NSLog(@"deadlock"); }); });

這樣是不會死鎖的,并且serialQueue和serialQueue2是在同一個線程中的惭笑。

隊(duì)列的知識也需要再看看B履贰!沉噩!

42\. iOS 多線程面試題(dispatch_barrier_async)

1捺宗、問:怎么用GCD實(shí)現(xiàn)多讀單寫?

多讀單寫的意思就是:可以多個讀者同時讀取數(shù)據(jù)川蒙,而在讀的時候蚜厉,不能取寫入數(shù)據(jù)。并且畜眨,在寫的過程中昼牛,不能有其他寫者去寫。即讀者之間是并發(fā)的康聂,寫者與讀者或其他寫者是互斥的贰健。

這里的寫處理就是通過柵欄的形式去寫。

就可以用dispatch_barrier_sync(柵欄函數(shù))去實(shí)現(xiàn)

- (id)readDataForKey:(NSString *)key { __block id result; dispatch_sync(_concurrentQueue, ^{ result = [self valueForKey:key]; }); return result; } - (void)writeData:(id)data forKey:(NSString *)key { dispatch_barrier_async(_concurrentQueue, ^{ [self setValue:data forKey:key]; }); }

dispatch_barrier_async全部知識:[http://www.reibang.com/p/540c2b22ba38](http://www.reibang.com/p/540c2b22ba38)

_concurrentQueue目的是柵欄函數(shù)和同步公用一個并發(fā)隊(duì)列T缈佟v印!

43.iOS 多線程面試題(dispatch_group_async)

場景:在n個耗時并發(fā)任務(wù)都完成后,再去執(zhí)行接下來的任務(wù)悬垃。比如游昼,在n個網(wǎng)絡(luò)請求完成后去刷新UI頁面。

dispatch_queue_t concurrentQueue = dispatch_queue_create("test1", DISPATCH_QUEUE_CONCURRENT); dispatch_group_t group = dispatch_group_create(); for (NSInteger i = 0; i < 10; i++) { dispatch_group_async(group, concurrentQueue, ^{ sleep(1); NSLog(@"%zd:網(wǎng)絡(luò)請求",i); }); } dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"刷新頁面"); });

注意最后要寫:dispatch_group_notify(group, dispatch_get_main_queue(), ^{

NSLog(@"刷新頁面");

});3⑷洹:嫱恪!?幢恕@扰濉!>搁拧1瓿!

44.iOS 多線程面試題(Dispatch Semaphore 信號量)

[http://www.reibang.com/p/8549a35b7bf2](http://www.reibang.com/p/8549a35b7bf2)

45.iOS 多線程面試題(延時函數(shù)(dispatch_after))

dispatch_after能讓我們添加進(jìn)隊(duì)列的任務(wù)延時執(zhí)行茁计,該函數(shù)并不是在指定時間后執(zhí)行處理料皇,而只是在指定時間追加處理到dispatch_queue

//第一個參數(shù)是time,第二個參數(shù)是dispatch_queue星压,第三個參數(shù)是要執(zhí)行的block dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"dispatch_after"); });

由于其內(nèi)部使用的是dispatch_time_t管理時間践剂,而不是NSTimer。

所以如果在子線程中調(diào)用娜膘,相比performSelector:afterDelay,不用關(guān)心runloop是否開啟

46.iOS 多線程面試題(使用dispatch_once實(shí)現(xiàn)單例)

多寫幾遍背下來Q犯!竣贪!

+ (instancetype)shareInstance { static dispatch_once_t onceToken; static id instance = nil; dispatch_once(&onceToken, ^{ instance = [[self alloc] init]; }); return instance; }

47.iOS 多線程面試題(NSThread+runloop實(shí)現(xiàn)常駐線程)

[http://www.reibang.com/p/f0bcc10abad0](http://www.reibang.com/p/f0bcc10abad0)

48.iOS 多線程面試題(自旋鎖與互斥鎖)

[http://www.reibang.com/p/80043c824d2d](http://www.reibang.com/p/80043c824d2d)

49.iOS 內(nèi)存管理面試題(在 MRC 下如何重寫屬性的 Setter 军洼、 Getter、strong贾富、retain歉眷、copy)

strong、retain颤枪、copy待補(bǔ)充:辜瘛!畏纲!

-(void)setBrand:(NSString *)brand{ //如果實(shí)例變量指向的地址和參數(shù)指向的地址不同 if (_brand != brand) { //將實(shí)例變量的引用計(jì)數(shù)減一 [_brand release]; //將參數(shù)變量的引用計(jì)數(shù)加一,并賦值給實(shí)例變量 _brand = [brand retain]; } }

-(NSString *)brand{ //將實(shí)例變量的引用計(jì)數(shù)加1后,添加自動減1 //作用,保證調(diào)用getter方法取值時可以取到值的同時在完全不需要使用后釋放 return [[_brand retain] autorelease]; }

//MRC下 手動釋放內(nèi)存 可重寫dealloc但不要調(diào)用dealloc 會崩潰 -(void)dealloc{ [_string release]; //必須最后調(diào)用super dealloc [super dealloc]; }

50.iOS 內(nèi)存管理面試題(循環(huán)引用)

[http://www.reibang.com/p/33b7b326dcc4](http://www.reibang.com/p/33b7b326dcc4)

2扇住、NSTimer循環(huán)引用屬于相互循環(huán)使用

在控制器內(nèi),創(chuàng)建NSTimer作為其屬性盗胀,由于定時器創(chuàng)建后也會強(qiáng)引用該控制器對象艘蹋,那么該對象和定時器就相互循環(huán)引用了。

如何解決呢票灰?

這里我們可以使用手動斷開循環(huán)引用:

如果是不重復(fù)定時器女阀,在回調(diào)方法里將定時器invalidate并置為nil即可宅荤。

如果是重復(fù)定時器,在合適的位置將其invalidate并置為nil即可

51.iOS 內(nèi)存管理面試題(說一下什么是 `懸垂指針`浸策?什么是 `野指針`?)

指針指向的內(nèi)存已經(jīng)被釋放了冯键,但是指針還存在,這就是一個 懸垂指針 或者說 迷途指針

沒有進(jìn)行初始化的指針庸汗,其實(shí)都是 野指針

52.iOS 內(nèi)存管理面試題(是否了解 深拷貝 和 淺拷貝 的概念惫确,集合類深拷貝如何實(shí)現(xiàn))

簡而言之:

1、對不可變的非集合對象蚯舱,copy是指針拷貝改化,mutablecopy是內(nèi)容拷貝

2、對于可變的非集合對象枉昏,copy陈肛,mutablecopy都是內(nèi)容拷貝

3、對不可變的數(shù)組凶掰、字典燥爷、集合等集合類對象,copy是指針拷貝懦窘,mutablecopy是內(nèi)容拷貝

4、對于可變的數(shù)組稚配、字典畅涂、集合等集合類對象,copy道川,mutablecopy都是內(nèi)容拷貝

但是午衰,對于集合對象的內(nèi)容復(fù)制僅僅是對對象本身,但是對象的里面的元素還是指針復(fù)制冒萄。要想復(fù)制整個集合對象臊岸,就要用集合深復(fù)制的方法,有兩種:W鹆鳌KЫ洹!Q录肌B咦 !S住瞎访!

(1)使用initWithArray:copyItems:方法,將第二個參數(shù)設(shè)置為YES即可

NSDictionary shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:YES];

(2)將集合對象進(jìn)行歸檔(archive)然后解歸檔(unarchive):

NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];

53.iOS 內(nèi)存相關(guān)面試題(能不能簡述一下 Dealloc 的實(shí)現(xiàn)機(jī)制.md)

[http://www.reibang.com/p/c00e2fdb5afb](http://www.reibang.com/p/c00e2fdb5afb)

54.iOS 內(nèi)存相關(guān)面試題(內(nèi)存中的5大區(qū)分別是什么吁恍?)

背下來0墙铡2パ荨!

棧區(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)制代碼寺惫。

55.iOS 內(nèi)存管理面試題(內(nèi)存管理默認(rèn)的關(guān)鍵字是什么疹吃?)

atomic,readWrite,strong如果改為基本數(shù)據(jù)類型,那就是atomic,readWrite, assign西雀。

56.iOS 內(nèi)存管理面試題(內(nèi)存管理方案)

內(nèi)存管理方案

*   taggedPointer :存儲小對象如NSNumber萨驶。深入理解Tagged Pointer
*   NONPOINTER_ISA(非指針型的isa):在64位架構(gòu)下,isa指針是占64比特位的艇肴,實(shí)際上只有30多位就已經(jīng)夠用了腔呜,為了提高利用率,剩余的比特位存儲了內(nèi)存管理的相關(guān)數(shù)據(jù)內(nèi)容再悼。
*   散列表:復(fù)雜的數(shù)據(jù)結(jié)構(gòu)核畴,包括了引用計(jì)數(shù)表和弱引用表

通過SideTables()結(jié)構(gòu)來實(shí)現(xiàn)的,SideTables()結(jié)構(gòu)下冲九,有很多SideTable的數(shù)據(jù)結(jié)構(gòu)谤草。

而sideTable當(dāng)中包含了自旋鎖,引用計(jì)數(shù)表娘侍,弱引用表咖刃。

SideTables()實(shí)際上是一個哈希表,通過對象的地址來計(jì)算該對象的引用計(jì)數(shù)在哪個sideTable中憾筏。

自旋鎖:

*   自旋鎖是“忙等”的鎖嚎杨。
*   適用于輕量訪問。

引用計(jì)數(shù)表和弱引用表實(shí)際是一個哈希表氧腰,來提高查找效率枫浙。

搜索一下hash表E偎唷!箩帚!

57.iOS 內(nèi)存管理面試題(內(nèi)存布局)

棧(stack):方法調(diào)用真友,局部變量等,是連續(xù)的紧帕,高地址往低地址擴(kuò)展

堆(heap):通過alloc等分配的對象盔然,是離散的,低地址往高地址擴(kuò)展是嗜,需要我們手動控制

未初始化數(shù)據(jù)(bss):未初始化的全局變量等

已初始化數(shù)據(jù)(data):已初始化的全局變量等

代碼段(text):程序代碼

3愈案、static、const和sizeof關(guān)鍵字

static關(guān)鍵字

答:Static的用途主要有兩個鹅搪,一是用于修飾存儲類型使之成為靜態(tài)存儲類型站绪,二是用于修飾鏈接屬性使之成為內(nèi)部鏈接屬性。

*   1丽柿、靜態(tài)存儲類型:

在函數(shù)內(nèi)定義的靜態(tài)局部變量恢准,該變量存在內(nèi)存的靜態(tài)區(qū),所以即使該函數(shù)運(yùn)行結(jié)束甫题,靜態(tài)變量的值不會被銷毀馁筐,函數(shù)下次運(yùn)行時能仍用到這個值。

在函數(shù)外定義的靜態(tài)變量——靜態(tài)全局變量幔睬,該變量的作用域只能在定義該變量的文件中眯漩,不能被其他文件通過extern引用。

*   2麻顶、內(nèi)部鏈接屬性

靜態(tài)函數(shù)只能在聲明它的源文件中使用。

const關(guān)鍵字

*   1舱卡、聲明常變量辅肾,使得指定的變量不能被修改。

const int a = 5;/*a的值一直為5轮锥,不能被改變*/ const int b; b = 10;/*b的值被賦值為10后矫钓,不能被改變*/ const int *ptr; /*ptr為指向整型常量的指針,ptr的值可以修改舍杜,但不能修改其所指向的值*/ int *const ptr;/*ptr為指向整型的常量指針新娜,ptr的值不能修改,但可以修改其所指向的值*/ const int *const ptr;/*ptr為指向整型常量的常量指針既绩,ptr及其指向的值都不能修改*/const int a = 5;/*a的值一直為5概龄,不能被改變*/ const int b; b = 10;/*b的值被賦值為10后,不能被改變*/ const int *ptr; /*ptr為指向整型常量的指針饲握,ptr的值可以修改私杜,但不能修改其所指向的值*/ int *const ptr;/*ptr為指向整型的常量指針蚕键,ptr的值不能修改,但可以修改其所指向的值*/ const int *const ptr;/*ptr為指向整型常量的常量指針衰粹,ptr及其指向的值都不能修改*/

*   2锣光、修飾函數(shù)形參,使得形參在函數(shù)內(nèi)不能被修改铝耻,表示輸入?yún)?shù)誊爹。

int fun(const int a);或int fun(const char *str);

*   3疫剃、修飾函數(shù)返回值彼宠,使得函數(shù)的返回值不能被修改。

const char *getstr(void);使用:const *str= getstr(); const int getint(void); 使用:const int a =getint();

sizeof關(guān)鍵字

sizeof是在編譯階段處理绎晃,且不能被編譯為機(jī)器碼泊柬。sizeof的結(jié)果等于對象或類型所占的內(nèi)存字節(jié)數(shù)椎镣。sizeof的返回值類型為size_t。

*   變量:int a; sizeof(a)為4兽赁;
*   指針:int *p; sizeof(p)為4状答;
*   數(shù)組:int b[10]; sizeof(b)為數(shù)組的大小,4*10刀崖;int c[0]; sizeof(c)等于0
*   結(jié)構(gòu)體:struct (int a; char ch;)s1; sizeof(s1)為8 與結(jié)構(gòu)體字節(jié)對齊有關(guān)惊科。

對結(jié)構(gòu)體求sizeof時,有兩個原則:

(1)展開后的結(jié)構(gòu)體的第一個成員的偏移量應(yīng)當(dāng)是被展開的結(jié)構(gòu)體中最大的成員的整數(shù)倍亮钦。 (2)結(jié)構(gòu)體大小必須是所有成員大小的整數(shù)倍馆截,這里所有成員計(jì)算的是展開后的成員,而不是將嵌套的結(jié)構(gòu)體當(dāng)做一個整體蜂莉。

*   注意:不能對結(jié)構(gòu)體中的位域成員使用sizeof

*   sizeof(void)等于1

*   sizeof(void *)等于4

58.iOS 內(nèi)存管理面試題(講一下 `iOS` 內(nèi)存管理的理解)

實(shí)際上是三種方案的結(jié)合

*   1.TaggedPointer(針對類似于 NSNumber的小對象類型)
*   2.NONPOINTER_ISA(64位系統(tǒng)下)

*   第一位的 0或 1代表是純地址型 isa指針蜡娶,還是 NONPOINTER_ISA指針。
*   第二位映穗,代表是否有關(guān)聯(lián)對象
*   第三位代表是否有 C++代碼窖张。
*   接下來33位代表指向的內(nèi)存地址
*   接下來有 弱引用的標(biāo)記
*   接下來有是否 delloc的標(biāo)記....等等

*   3.散列表(引用計(jì)數(shù)表、weak表)

*   SideTables表在 非嵌入式的64位系統(tǒng)中蚁滋,有 64張 SideTable表
*   每一張 SideTable主要是由三部分組成宿接。自旋鎖、引用計(jì)數(shù)表辕录、弱引用表睦霎。
*   全局的 引用計(jì)數(shù)之所以不存在同一張表中,是為了避免資源競爭走诞,解決效率的問題副女。
*   引用計(jì)數(shù)表中引入了 分離鎖的概念,將一張表分拆成多個部分速梗,對他們分別加鎖肮塞,可以實(shí)現(xiàn)并發(fā)操作襟齿,提升執(zhí)行效率

搜一下散列表!U碚浴猜欺!

59.iOS 內(nèi)存管理面試題(講一下 `@dynamic` 關(guān)鍵字?)

@dynamic 意味著編譯器不會幫助我們自動合成 setter 和 getter 方法拷窜。我們需要手動實(shí)現(xiàn)开皿、這里就涉及到 Runtime 的動態(tài)添加方法的知識點(diǎn)。

60.iOS 內(nèi)存管理面試題(簡要說一下 `@autoreleasePool` 的數(shù)據(jù)結(jié)構(gòu)篮昧?)

簡單說是雙向鏈表赋荆,每張鏈表頭尾相接,有 parent懊昨、child指針

每創(chuàng)建一個池子窄潭,會在首部創(chuàng)建一個 哨兵對象,作為標(biāo)記

最外層池子的頂端會有一個next指針。當(dāng)鏈表容量滿了酵颁,就會在鏈表的頂端嫉你,并指向下一張表。

61.iOS 內(nèi)存管理面試題(訪問 `__weak` 修飾的變量躏惋,是否已經(jīng)被注冊在了 `@autoreleasePool` 中幽污?為什么?)

答案是肯定的簿姨,__weak修飾的變量屬于弱引用距误,如果沒有被注冊到 @autoreleasePool中,創(chuàng)建之后也就會隨之銷毀扁位,為了延長它的生命周期准潭,必須注冊到 @autoreleasePool中,以延緩釋放域仇。

62.iOS 內(nèi)存管理面試題(`retain`惋鹅、`release` 的實(shí)現(xiàn)機(jī)制?)

1.Retain的實(shí)現(xiàn)機(jī)制殉簸。

SideTable& table = SideTables()[This];//第一層 hash算法,找到 指針變量所對應(yīng)的 sideTable size_t& refcntStorage = table.refcnts[This];//然后再通過一層 hash算法沽讹,找到存儲 引用計(jì)數(shù)的 size_t refcntStorage += SIZE_TABLE_RC_ONE;//IZE_TABLE_RC_ONE是一個宏定義般卑,實(shí)際上是一個值為 4 的偏移量。

2.Release的實(shí)現(xiàn)機(jī)制爽雄。

SideTable& table = SideTables()[This];//第一層 hash算法蝠检,找到 指針變量所對應(yīng)的 sideTable size_t& refcntStorage = table.refcnts[This];//然后再通過一層 hash算法,找到存儲 引用計(jì)數(shù)的 size_t refcntStorage -= SIZE_TABLE_RC_ONE;//IZE_TABLE_RC_ONE是一個宏定義挚瘟,實(shí)際上是一個值為 4 的偏移量叹谁。

二者的實(shí)現(xiàn)機(jī)制類似饲梭,概括講就是通過第一層 hash算法,找到 指針變量所對應(yīng)的 sideTable焰檩。然后再通過一層 hash算法憔涉,找到存儲 引用計(jì)數(shù)的 size_t,然后對其進(jìn)行增減操作析苫。retainCount不是固定的 1兜叨,SIZE_TABLE_RC_ONE是一個宏定義,實(shí)際上是一個值為 4 的偏移量衩侥。

63.iOS 內(nèi)存管理面試題(MRC(手動引用計(jì)數(shù))和ARC(自動引用計(jì)數(shù)))

MRC(手動引用計(jì)數(shù))和ARC(自動引用計(jì)數(shù))

1国旷、MRC:alloc,retain茫死,release跪但,retainCount,autorelease,dealloc

2、ARC:

*   ARC是LLVM和Runtime協(xié)作的結(jié)果
*   ARC禁止手動調(diào)用retain峦萎,release屡久,retainCount,autorelease關(guān)鍵字
*   ARC新增weak,strong關(guān)鍵字

3骨杂、引用計(jì)數(shù)管理:

*   alloc: 經(jīng)過一系列函數(shù)調(diào)用涂身,最終調(diào)用了calloc函數(shù),這里并沒有設(shè)置引用計(jì)數(shù)為1
*   retain: 經(jīng)過兩次哈希查找搓蚪,找到其對應(yīng)引用計(jì)數(shù)值蛤售,然后將引用計(jì)數(shù)加1(實(shí)際是加偏移量)
*   release:和retain相反,經(jīng)過兩次哈希查找妒潭,找到其對應(yīng)引用計(jì)數(shù)值悴能,然后將引用計(jì)數(shù)減1
*   dealloc:

4、弱引用管理:

*   添加weak變量:通過哈希算法位置查找添加雳灾。如果查找對應(yīng)位置中已經(jīng)有了當(dāng)前對象所對應(yīng)的弱引用數(shù)組漠酿,就把新的弱引用變量添加到數(shù)組當(dāng)中;如果沒有谎亩,就創(chuàng)建一個弱引用數(shù)組炒嘲,并將該弱引用變量添加到該數(shù)組中。
*   當(dāng)一個被weak修飾的對象被釋放后匈庭,weak對象怎么處理的夫凸?

清除weak變量,同時設(shè)置指向?yàn)閚il阱持。當(dāng)對象被dealloc釋放后夭拌,在dealloc的內(nèi)部實(shí)現(xiàn)中,會調(diào)用弱引用清除的相關(guān)函數(shù),會根據(jù)當(dāng)前對象指針查找弱引用表鸽扁,找到當(dāng)前對象所對應(yīng)的弱引用數(shù)組蒜绽,將數(shù)組中的所有弱引用指針都置為nil。

5桶现、自動釋放池:

在當(dāng)次runloop將要結(jié)束的時候調(diào)用objc_autoreleasePoolPop躲雅,并push進(jìn)來一個新的AutoreleasePool

AutoreleasePoolPage是以棧為結(jié)點(diǎn)通過雙向鏈表的形式組合而成,是和線程一一對應(yīng)的巩那。

內(nèi)部屬性有parent吏夯,child對應(yīng)前后兩個結(jié)點(diǎn),thread對應(yīng)線程 即横,next指針指向棧中下一個可填充的位置噪生。

*   AutoreleasePool實(shí)現(xiàn)原理?

編譯器會將 @autoreleasepool {} 改寫為:

void * ctx = objc_autoreleasePoolPush; {} objc_autoreleasePoolPop(ctx);

objc_autoreleasePoolPush:

把當(dāng)前next位置置為nil东囚,即哨兵對象,然后next指針指向下一個可入棧位置跺嗽,

AutoreleasePool的多層嵌套,即每次objc_autoreleasePoolPush页藻,實(shí)際上是不斷地向棧中插入哨兵對象桨嫁。

objc_autoreleasePoolPop:

根據(jù)傳入的哨兵對象找到對應(yīng)位置。

給上次push操作之后添加的對象依次發(fā)送release消息份帐。

回退next指針到正確的位置璃吧。

64.iOS 內(nèi)存管理面試題(`BAD_ACCESS` 在什么情況下出現(xiàn)? )

訪問了已經(jīng)被銷毀的內(nèi)存空間,就會報出這個錯誤废境。

根本原因是有 懸垂指針 沒有被釋放畜挨。

65.iOS 內(nèi)存管理面試題(`autoReleasePool` 什么時候釋放?)

[http://www.reibang.com/p/73150489071e](http://www.reibang.com/p/73150489071e)

66.iOS 內(nèi)存管理面試題( `ARC` 在運(yùn)行時做了哪些工作?)

*   主要是指 weak關(guān)鍵字噩凹。weak修飾的變量能夠在引用計(jì)數(shù)為0時被自動設(shè)置成 nil巴元,顯然是有運(yùn)行時邏輯在工作的。

*   為了保證向后兼容性驮宴,ARC在運(yùn)行時檢測到類函數(shù)中的 autorelease后緊跟其后 retain逮刨,此時不直接調(diào)用對象的 autorelease方法,而是改為調(diào)用 objc_autoreleaseReturnValue堵泽。

objc_autoreleaseReturnValue會檢視當(dāng)前方法返回之后即將要執(zhí)行的那段代碼修己,若那段代碼要在返回對象上執(zhí)行 retain操作,則設(shè)置全局?jǐn)?shù)據(jù)結(jié)構(gòu)中的一個標(biāo)志位迎罗,而不執(zhí)行 autorelease操作箩退,與之相似,如果方法返回了一個自動釋放的對象佳谦,而調(diào)用方法的代碼要保留此對象,那么此時不直接執(zhí)行 retain滋戳,而是改為執(zhí)行 objc_retainAoutoreleasedReturnValue函數(shù)钻蔑。此函數(shù)要檢測剛才提到的標(biāo)志位啥刻,若已經(jīng)置位,則不執(zhí)行 retain操作咪笑,設(shè)置并檢測標(biāo)志位可帽,要比調(diào)用 autorelease和retain更快。

67.iOS 內(nèi)存管理面試題(`ARC` 在編譯時做了哪些工作)

根據(jù)代碼執(zhí)行的上下文語境窗怒,在適當(dāng)?shù)奈恢貌迦?retain映跟,release

68.iOS 內(nèi)存管理面試題(`ARC` 的 `retainCount` 怎么存儲的?)

ARC的 retainCount怎么存儲的扬虚?

存在64張哈希表中努隙,根據(jù)哈希算法去查找所在的位置,無需遍歷辜昵,十分快捷

散列表(引用計(jì)數(shù)表荸镊、weak表)

- SideTables表在 非嵌入式的64位系統(tǒng)中,有 64張 SideTable表

- 每一張 SideTable主要是由三部分組成堪置。自旋鎖躬存、引用計(jì)數(shù)表、弱引用表舀锨。

- 全局的 引用計(jì)數(shù)之所以不存在同一張表中岭洲,是為了避免資源競爭,解決效率的問題坎匿。

- 引用計(jì)數(shù)表中引入了 分離鎖的概念盾剩,將一張表分拆成多個部分,對他們分別加鎖碑诉,可以實(shí)現(xiàn)并發(fā)操作彪腔,提升執(zhí)行效率

引用計(jì)數(shù)表(哈希表)

通過指針的地址,查找到引用計(jì)數(shù)的地址进栽,大大提升查找效率

通過 DisguisedPtr(objc_object)函數(shù)存儲德挣,同時也通過這個函數(shù)查找,這樣就避免了循環(huán)遍歷快毛。

69.iOS 內(nèi)存管理面試題(`__weak` 屬性修飾的變量格嗅,如何實(shí)現(xiàn)在變量沒有強(qiáng)引用后自動置為 `nil` ?)

用的弱引用 - weak表唠帝。也是一張 哈希表屯掖。

被 weak修飾的指針變量所指向的地址是 key,所有指向這塊內(nèi)存地址的指針會被添加在一個數(shù)組里襟衰,這個數(shù)組是 Value贴铜。當(dāng)內(nèi)存地址銷毀,數(shù)組里的所有對象被置為 nil。

70.iOS 內(nèi)存管理面試題( `__weak` 和 `_Unsafe_Unretain` 的區(qū)別绍坝?)

weak 修飾的指針變量徘意,在指向的內(nèi)存地址銷毀后,會在 Runtime的機(jī)制下轩褐,自動置為 nil椎咧。

_Unsafe_Unretain不會置為 nil,容易出現(xiàn) 懸垂指針把介,發(fā)生崩潰勤讽。但是 _Unsafe_Unretain比 __weak效率高。

71.iOS 設(shè)計(jì)模式面試題(編程中的六大設(shè)計(jì)原則拗踢?)

背下來=烹埂!秒拔!

1.單一職責(zé)原則

通俗地講就是一個類只做一件事

*   CALayer:動畫和視圖的顯示莫矗。
*   UIView:只負(fù)責(zé)事件傳遞、事件響應(yīng)砂缩。

2.開閉原則

對修改關(guān)閉作谚,對擴(kuò)展開放。

要考慮到后續(xù)的擴(kuò)展性庵芭,而不是在原有的基礎(chǔ)上來回修改

3.接口隔離原則

使用多個專門的協(xié)議妹懒、而不是一個龐大臃腫的協(xié)議

*   UITableviewDelegate
*   UITableViewDataSource

4.依賴倒置原則

抽象不應(yīng)該依賴于具體實(shí)現(xiàn)、具體實(shí)現(xiàn)可以依賴于抽象双吆。

調(diào)用接口感覺不到內(nèi)部是如何操作的

5.里氏替換原則

父類可以被子類無縫替換眨唬,且原有的功能不受任何影響

例如 KVO

6.迪米特法則

一個對象應(yīng)當(dāng)對其他對象盡可能少的了解,實(shí)現(xiàn)高聚合好乐、低耦合

72.RSA非對稱加密和MD5加密

補(bǔ)充MD5加密及其應(yīng)用X腋汀!蔚万!

RSA非對稱加密

對稱加密[算法]在加密和解密時使用的是同一個秘鑰岭妖;而[非對稱加密算法]需要兩個[密鑰]來進(jìn)行加密和解密,這兩個秘鑰是[公開密鑰](public key反璃,簡稱公鑰)和私有密鑰(private key昵慌,簡稱私鑰)。

RSA加密

與對稱加密[算法]不同淮蜈,[非對稱加密算法]需要兩個[密鑰]:[公開密鑰](publickey)和私有密鑰(privatekey)斋攀。公開密鑰與私有密鑰是一對,如果用公開密鑰對數(shù)據(jù)進(jìn)行加密梧田,只有用對應(yīng)的私有密鑰才能解密淳蔼;如果用私有密鑰對數(shù)據(jù)進(jìn)行加密侧蘸,那么只有用對應(yīng)的公開密鑰才能解密。因?yàn)榧用芎徒饷苁褂玫氖莾蓚€不同的[密鑰]肖方,所以這種算法叫作[非對稱加密算法]闺魏。

RSA加密原理

RSA是常用的加密模式,其加密原理可用以下的例子進(jìn)行簡要的論述俯画。

隨機(jī)取兩個質(zhì)數(shù)

P = 61; q = 53; N = P * Q = 3233; // E是1-n之間的一個隨機(jī)的質(zhì)數(shù) E = 17; // D是通過一系列數(shù)學(xué)運(yùn)算得出的一個數(shù)字, // 運(yùn)算方法后續(xù)會附上阮一峰老師的兩篇文章鏈接 // (N,D)(N,E)要滿足可以互相解值運(yùn)算 // 假如(N,D)是公鑰司草,(N,E)是私鑰 // 滿足私鑰加密艰垂,公鑰解密或者反過來公鑰加密,私鑰解密埋虹。 // 也要滿足只知道(N猜憎,D)就想知道(N,E),那就要把N這個大的整數(shù)進(jìn)行因數(shù)分解搔课。 // 因數(shù)分解只能使用暴力窮舉胰柑,N越大,相應(yīng)的也就越安全 // 當(dāng) N 大到1024位或者2048位時爬泥,以目前的技術(shù)破解幾乎不可能柬讨,所以很安全

73.簡述 `SSL` 加密的過程用了哪些加密方法,為何這么作袍啡?

簡述 SSL加密的過程用了哪些加密方法踩官,為何這么作?

SSL加密的過程之前有些過境输,此處不再贅述蔗牡。

SSL加密,在過程中實(shí)際使用了 對稱加密和 非對稱加密的結(jié)合嗅剖。

主要的考慮是先使用 非對稱加密進(jìn)行連接辩越,這樣做是為了避免中間人攻擊秘鑰被劫持,但是 非對稱加密的效率比較低信粮。所以一旦建立了安全的連接之后黔攒,就可以使用輕量的 對稱加密。

74.iOS 性能優(yōu)化面試題

在性能優(yōu)化中一個最具參考價值的屬性是FPS:Frames Per Second,其實(shí)就是屏幕刷新率蒋院,蘋果的iphone推薦的刷新率是60Hz亏钩,也就是說GPU每秒鐘刷新屏幕60次,這每刷新一次就是一幀frame欺旧,F(xiàn)PS也就是每秒鐘刷新多少幀畫面姑丑。靜止不變的頁面FPS值是0,這個值是沒有參考意義的辞友,只有當(dāng)頁面在執(zhí)行動畫或者滑動的時候栅哀,F(xiàn)PS值才具有參考價值震肮,F(xiàn)PS值的大小體現(xiàn)了頁面的流暢程度高低,當(dāng)?shù)陀?5的時候卡頓會比較明顯留拾。

圖層混合:

每一個layer是一個紋理戳晌,所有的紋理都以某種方式堆疊在彼此的頂部。對于屏幕上的每一個像素痴柔,GPU需要算出怎么混合這些紋理來得到像素RGB的值沦偎。

當(dāng)Sa = 0.5時,RGB值為(0.5, 0, 0)咳蔚,可以看出豪嚎,當(dāng)兩個不是完全不透明的CALayer覆蓋在一起時,GPU大量做這種復(fù)合操作,隨著這中操作的越多谈火,GPU越忙碌侈询,性能肯定會受到影響。

公式:

R = S + D * ( 1 – Sa )

結(jié)果的顏色是源色彩(頂端紋理)+目標(biāo)顏色(低一層的紋理)*(1-源顏色的透明度)糯耍。

當(dāng)Sa = 1時扔字,R = S,GPU將不會做任何合成,而是簡單從這個層拷貝温技,不需要考慮它下方的任何東西(因?yàn)槎急凰趽踝×?革为,這節(jié)省了GPU相當(dāng)大的工作量。

一荒揣、入門級

1篷角、用ARC管理內(nèi)存

2、在正確的地方使用 reuseIdentifier

3系任、盡量把views設(shè)置為透明

4恳蹲、避免過于龐大的XIB

5、不要阻塞主線程

6俩滥、在ImageViews中調(diào)整圖片大小嘉蕾。如果要在UIImageView中顯示一個來自bundle的圖片,你應(yīng)保證圖片的大小和UIImageView的大小相同霜旧。在運(yùn)行中縮放圖片是很耗費(fèi)資源的错忱,特別是UIImageView嵌套在UIScrollView中的情況下。如果圖片是從遠(yuǎn)端服務(wù)加載的你不能控制圖片大小挂据,比如在下載前調(diào)整到合適大小的話以清,你可以在下載完成后,最好是用background

thread崎逃,縮放一次掷倔,然后在UIImageView中使用縮放后的圖片。

7个绍、選擇正確的Collection勒葱。

*   Arrays: 有序的一組值浪汪。使用index來lookup很快,使用value lookup很慢凛虽, 插入/刪除很慢死遭。
*   Dictionaries: 存儲鍵值對。 用鍵來查找比較快凯旋。
*   Sets: 無序的一組值呀潭。用值來查找很快,插入/刪除很快至非。

8蜗侈、打開gzip壓縮。app可能大量依賴于服務(wù)器資源睡蟋,問題是我們的目標(biāo)是移動設(shè)備,因此你就不能指望網(wǎng)絡(luò)狀況有多好枷颊。減小文檔的一個方式就是在服務(wù)端和你的app中打開gzip戳杀。這對于文字這種能有更高壓縮率的數(shù)據(jù)來說會有更顯著的效用。

iOS已經(jīng)在NSURLConnection中默認(rèn)支持了gzip壓縮夭苗,當(dāng)然AFNetworking這些基于它的框架亦然信卡。

二、中級

1题造、重用和延遲加載(lazy load) Views

*   更多的view意味著更多的渲染傍菇,也就是更多的CPU和內(nèi)存消耗,對于那種嵌套了很多view在UIScrollView里邊的app更是如此界赔。
*   這里我們用到的技巧就是模仿UITableView和UICollectionView的操作: 不要一次創(chuàng)建所有的subview丢习,而是當(dāng)需要時才創(chuàng)建,當(dāng)它們完成了使命淮悼,把他們放進(jìn)一個可重用的隊(duì)列中咐低。這樣的話你就只需要在滾動發(fā)生時創(chuàng)建你的views,避免了不劃算的內(nèi)存分配袜腥。

2见擦、Cache, Cache, 還是Cache!

*   一個極好的原則就是,緩存所需要的羹令,也就是那些不大可能改變但是需要經(jīng)常讀取的東西鲤屡。
*   我們能緩存些什么呢?一些選項(xiàng)是福侈,遠(yuǎn)端服務(wù)器的響應(yīng)酒来,圖片,甚至計(jì)算結(jié)果癌刽,比如UITableView的行高役首。
*   NSCache和NSDictionary類似尝丐,不同的是系統(tǒng)回收內(nèi)存的時候它會自動刪掉它的內(nèi)容。

3衡奥、權(quán)衡渲染方法.性能能還是要bundle保持合適的大小爹袁。

4、處理內(nèi)存警告.移除對緩存矮固,圖片object和其他一些可以重創(chuàng)建的objects的strong references.

5失息、重用大開銷對象

6、一些objects的初始化很慢档址,比如NSDateFormatter和NSCalendar盹兢。然而,你又不可避免地需要使用它們守伸,比如從JSON或者XML中解析數(shù)據(jù)绎秒。想要避免使用這個對象的瓶頸你就需要重用他們,可以通過添加屬性到你的class里或者創(chuàng)建靜態(tài)變量來實(shí)現(xiàn)尼摹。

7见芹、避免反復(fù)處理數(shù)據(jù).在服務(wù)器端和客戶端使用相同的數(shù)據(jù)結(jié)構(gòu)很重要。

8蠢涝、選擇正確的數(shù)據(jù)格式.解析JSON會比XML更快一些玄呛,JSON也通常更小更便于傳輸。從iOS5起有了官方內(nèi)建的JSON deserialization 就更加方便使用了和二。但是XML也有XML的好處徘铝,比如使用SAX 來解析XML就像解析本地文件一樣,你不需像解析json一樣等到整個文檔下載完成才開始解析惯吕。當(dāng)你處理很大的數(shù)據(jù)的時候就會極大地減低內(nèi)存消耗和增加性能惕它。

9、正確設(shè)定背景圖片

*   全屏背景圖混埠,在view中添加一個UIImageView作為一個子View
*   只是某個小的view的背景圖怠缸,你就需要用UIColor的colorWithPatternImage來做了,它會更快地渲染也不會花費(fèi)很多內(nèi)存:

10钳宪、減少使用Web特性揭北。想要更高的性能你就要調(diào)整下你的HTML了。第一件要做的事就是盡可能移除不必要的javascript吏颖,避免使用過大的框架搔体。能只用原生js就更好了。盡可能異步加載例如用戶行為統(tǒng)計(jì)script這種不影響頁面表達(dá)的javascript半醉。注意你使用的圖片疚俱,保證圖片的符合你使用的大小。

11缩多、Shadow Path 呆奕。CoreAnimation不得不先在后臺得出你的圖形并加好陰影然后才渲染养晋,這開銷是很大的。使用shadowPath的話就避免了這個問題梁钾。使用shadow path的話iOS就不必每次都計(jì)算如何渲染绳泉,它使用一個預(yù)先計(jì)算好的路徑崭捍。但問題是自己計(jì)算path的話可能在某些View中比較困難葱弟,且每當(dāng)view的frame變化的時候你都需要去update shadow path.

12岸军、優(yōu)化Table View

*   正確使用reuseIdentifier來重用cells
*   盡量使所有的view opaque耸成,包括cell自身
*   避免漸變,圖片縮放霉赡,后臺選人
*   緩存行高
*   如果cell內(nèi)現(xiàn)實(shí)的內(nèi)容來自web装处,使用異步加載离钝,緩存請求結(jié)果
*   使用shadowPath來畫陰影
*   減少subviews的數(shù)量
*   盡量不適用cellForRowAtIndexPath:方咆,如果你需要用到它月腋,只用-一次然后緩存結(jié)果
*   使用正確的數(shù)據(jù)結(jié)構(gòu)來存儲數(shù)據(jù)
*   使用rowHeight, sectionFooterHeight 和 sectionHeaderHeight來設(shè)定固定的高,不要請求delegate

13瓣赂、選擇正確的數(shù)據(jù)存儲選項(xiàng)

*   NSUserDefaults的問題是什么罗售?雖然它很nice也很便捷,但是它只適用于小數(shù)據(jù)钩述,比如一些簡單的布爾型的設(shè)置選項(xiàng),再大點(diǎn)你就要考慮其它方式了
*   XML這種結(jié)構(gòu)化檔案呢穆碎?總體來說牙勘,你需要讀取整個文件到內(nèi)存里去解析,這樣是很不經(jīng)濟(jì)的所禀。使用SAX又是一個很麻煩的事情方面。
*   NSCoding?不幸的是色徘,它也需要讀寫文件恭金,所以也有以上問題。
*   在這種應(yīng)用場景下褂策,使用SQLite 或者 Core Data比較好横腿。使用這些技術(shù)你用特定的查詢語句就能只加載你需要的對象。
*   在性能層面來講斤寂,SQLite和Core Data是很相似的耿焊。他們的不同在于具體使用方法。
*   Core Data代表一個對象的graph model遍搞,但SQLite就是一個DBMS罗侯。
*   Apple在一般情況下建議使用Core Data,但是如果你有理由不使用它溪猿,那么就去使用更加底層的SQLite吧钩杰。
*   如果你使用SQLite纫塌,你可以用FMDB這個庫來簡化SQLite的操作,這樣你就不用花很多經(jīng)歷了解SQLite的C API了讲弄。

三措左、高級

1、加速啟動時間垂睬∠被模快速打開app是很重要的,特別是用戶第一次打開它時驹饺,對app來講钳枕,第一印象太太太重要了。你能做的就是使它盡可能做更多的異步任務(wù)赏壹,比如加載遠(yuǎn)端或者數(shù)據(jù)庫數(shù)據(jù)鱼炒,解析數(shù)據(jù)。避免過于龐大的XIB蝌借,因?yàn)樗麄兪窃谥骶€程上加載的昔瞧。所以盡量使用沒有這個問題的Storyboards吧!一定要把設(shè)備從Xcode斷開來測試啟動速度

2菩佑、使用Autorelease Pool自晰。NSAutoreleasePool`負(fù)責(zé)釋放block中的autoreleased objects。一般情況下它會自動被UIKit調(diào)用稍坯。但是有些狀況下你也需要手動去創(chuàng)建它酬荞。假如你創(chuàng)建很多臨時對象,你會發(fā)現(xiàn)內(nèi)存一直在減少直到這些對象被release的時候瞧哟。這是因?yàn)橹挥挟?dāng)UIKit用光了autorelease pool的時候memory才會被釋放混巧。消息是你可以在你自己的@autoreleasepool里創(chuàng)建臨時的對象來避免這個行為。

3勤揩、選擇是否緩存圖片咧党。常見的從bundle中加載圖片的方式有兩種,一個是用imageNamed陨亡,二是用imageWithContentsOfFile傍衡,第一種比較常見一點(diǎn)。

4负蠕、避免日期格式轉(zhuǎn)換聪舒。如果你要用NSDateFormatter來處理很多日期格式,應(yīng)該小心以待虐急。就像先前提到的箱残,任何時候重用NSDateFormatters都是一個好的實(shí)踐。如果你可以控制你所處理的日期格式,盡量選擇Unix時間戳被辑。你可以方便地從時間戳轉(zhuǎn)換到NSDate:

- (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp { return[NSDate dateWithTimeIntervalSince1970:timestamp]; }

這樣會比用C來解析日期字符串還快燎悍!需要注意的是,許多web API會以微秒的形式返回時間戳盼理,因?yàn)檫@種格式在javascript中更方便使用谈山。記住用dateFromUnixTimestamp之前除以1000就好了。

平時你是如何對代碼進(jìn)行性能優(yōu)化的宏怔?

*   利用性能分析工具檢測奏路,包括靜態(tài) Analyze 工具,以及運(yùn)行時 Profile 工具臊诊,通過Xcode工具欄中Product->Profile可以啟動,
*   比如測試程序啟動運(yùn)行時間鸽粉,當(dāng)點(diǎn)擊Time Profiler應(yīng)用程序開始運(yùn)行后.就能獲取到整個應(yīng)用程序運(yùn)行消耗時間分布和百分比.為了保證數(shù)據(jù)分析在統(tǒng)一使用場景真實(shí)需要注意一定要使用真機(jī),因?yàn)榇藭r模擬器是運(yùn)行在Mac上,而Mac上的CPU往往比iOS設(shè)備要快抓艳。
*   為了防止一個應(yīng)用占用過多的系統(tǒng)資源触机,開發(fā)iOS的蘋果工程師門設(shè)計(jì)了一個“看門狗”的機(jī)制。在不同的場景下玷或,“看門狗”會監(jiān)測應(yīng)用的性能儡首。如果超出了該場景所規(guī)定的運(yùn)行時間,“看門狗”就會強(qiáng)制終結(jié)這個應(yīng)用的進(jìn)程偏友。開發(fā)者們在crashlog里面蔬胯,會看到諸如0x8badf00d這樣的錯誤代碼。

優(yōu)化Table View

*   正確使用reuseIdentifier來重用cells
*   盡量使所有的view opaque位他,包括cell自身
*   如果cell內(nèi)現(xiàn)實(shí)的內(nèi)容來自web笔宿,使用異步加載,緩存請求結(jié)果

減少subviews的數(shù)量

*   盡量不適用cellForRowAtIndexPath:棱诱,如果你需要用到它,只用一次然后緩存結(jié)果
*   使用rowHeight, sectionFooterHeight和sectionHeaderHeight來設(shè)定固定的高涝动,不要請求delegate

UIImage加載圖片性能問題

*   imagedNamed初始化
*   imageWithContentsOfFile初始化
*   imageNamed默認(rèn)加載圖片成功后會內(nèi)存中緩存圖片,這個方法用一個指定的名字在系統(tǒng)緩存中查找并返回一個圖片對象.如果緩存中沒有找到相應(yīng)的圖片對象,則從指定地方加載圖片然后緩存對象迈勋,并返回這個圖片對象.
*   imageWithContentsOfFile則僅只加載圖片,不緩存.
*   加載一張大圖并且使用一次,用imageWithContentsOfFile是最好,這樣CPU不需要做緩存節(jié)約時間.
*   使用場景需要編程時醋粟,應(yīng)該根據(jù)實(shí)際應(yīng)用場景加以區(qū)分靡菇,UIimage雖小,但使用元素較多問題會有所凸顯.

*   不要在viewWillAppear 中做費(fèi)時的操作:viewWillAppear: 在view顯示之前被調(diào)用米愿,出于效率考慮厦凤,方法中不要處理復(fù)雜費(fèi)時操作;在該方法設(shè)置 view 的顯示屬性之類的簡單事情育苟,比如背景色较鼓,字體等。否則,會明顯感覺到 view 有卡頓或者延遲博烂。
*   在正確的地方使用reuseIdentifier:table view用 tableView:cellForRowAtIndexPath:為rows分配cells的時候香椎,它的數(shù)據(jù)應(yīng)該重用自UITableViewCell。
*   盡量把views設(shè)置為透明:如果你有透明的Views你應(yīng)該設(shè)置它們的opaque屬性為YES禽篱。系統(tǒng)用一個最優(yōu)的方式渲染這些views畜伐。這個簡單的屬性在IB或者代碼里都可以設(shè)定。
*   避免過于龐大的XIB:盡量簡單的為每個Controller配置一個單獨(dú)的XIB躺率,盡可能把一個View Controller的view層次結(jié)構(gòu)分散到單獨(dú)的XIB中去, 當(dāng)你加載一個引用了圖片或者聲音資源的nib時玛界,nib加載代碼會把圖片和聲音文件寫進(jìn)內(nèi)存。
*   不要阻塞主線程:永遠(yuǎn)不要使主線程承擔(dān)過多悼吱。因?yàn)閁IKit在主線程上做所有工作慎框,渲染,管理觸摸反應(yīng)舆绎,回應(yīng)輸入等都需要在它上面完成,大部分阻礙主進(jìn)程的情形是你的app在做一些牽涉到讀寫外部資源的I/O操作鲤脏,比如存儲或者網(wǎng)絡(luò)。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

// 選擇一個子線程來執(zhí)行耗時操作

dispatch_async(dispatch_get_main_queue(), ^{

// 返回主線程更新UI

});

});

*   在Image Views中調(diào)整圖片大小

如果要在UIImageView中顯示一個來自bundle的圖片吕朵,你應(yīng)保證圖片的大小和UIImageView的大小相同猎醇。在運(yùn)行中縮放圖片是很耗費(fèi)資源的.

講講你用Instrument優(yōu)化動畫性能的經(jīng)歷吧(別問我什么是Instrument)

Apple的instrument為開發(fā)者提供了各種template去優(yōu)化app性能和定位問題。很多公司都在趕feature努溃,并沒有充足的時間來做優(yōu)化硫嘶,導(dǎo)致不少開發(fā)者對instrument不怎么熟悉。但這里面其實(shí)涵蓋了非常完整的計(jì)算機(jī)基礎(chǔ)理論知識體系梧税,memory沦疾,disk,network第队,thread哮塞,cpu,gpu等等凳谦,順藤摸瓜去學(xué)習(xí)忆畅,是一筆巨大的知識財(cái)富。動畫性能只是其中一個template尸执,重點(diǎn)還是理解上面問題當(dāng)中CPU GPU如何配合工作的知識家凯。

facebook啟動時間優(yōu)化

1.瘦身請求依賴

2.UDP啟動請求先行緩存

3.隊(duì)列串行化處理啟動響應(yīng)

75.iOS 性能優(yōu)化面試題(光柵化)

光柵化是將幾何數(shù)據(jù)經(jīng)過一系列變換后最終轉(zhuǎn)換為像素,從而呈現(xiàn)在顯示設(shè)備上的過程如失,光柵化的本質(zhì)是坐標(biāo)變換绊诲、幾何離散化

我們使用 UITableView 和 UICollectionView 時經(jīng)常會遇到各個 Cell 的樣式是一樣的,這時候我們可以使用這個屬性提高性能:

cell.layer.shouldRasterize=YES;

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

76.iOS 性能優(yōu)化面試題(如何高性能的畫一個圓角褪贵?)

搜索一下直接切圖畫圓角掂之,貝塞爾曲線圓角!!板惑!

視圖和圓角的大小對幀率并沒有什么卵影響橄镜,數(shù)量才是傷害的核心輸出

label.layer.cornerRadius = 5 label.layer.masksToBounds = true

如果能夠只用 cornerRadius解決問題,就不用優(yōu)化冯乘。

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

UIImageView的圓角通過直接截取圖片實(shí)現(xiàn)姊氓,其它視圖的圓角可以通過 Core Graphics畫出圓角矩形實(shí)現(xiàn)。

[圖片上傳失敗...(image-56643b-1581791102540)]

77.iOS 性能優(yōu)化面試題(如何提升 `tableview` 的流暢度喷好?)

本質(zhì)上是降低 CPU翔横、GPU 的工作,從這兩個大的方面去提升性能梗搅。

*   CPU:對象的創(chuàng)建和銷毀禾唁、對象屬性的調(diào)整、布局計(jì)算无切、文本的計(jì)算和排版荡短、圖片的格式轉(zhuǎn)換和解碼、圖像的繪制
*   GPU:紋理的渲染

卡頓優(yōu)化在 CPU 層面

*   盡量用輕量級的對象哆键,比如用不到事件處理的地方掘托,可以考慮使用 CALayer 取代 UIView
*   不要頻繁地調(diào)用 UIView 的相關(guān)屬性,比如 frame籍嘹、bounds闪盔、transform 等屬性,盡量減少不必要的修改
*   盡量提前計(jì)算好布局辱士,在有需要時一次性調(diào)整對應(yīng)的屬性泪掀,不要多次修改屬性
*   Autolayout 會比直接設(shè)置 frame 消耗更多的 CPU 資源
*   圖片的 size 最好剛好跟 UIImageView 的 size 保持一致
*   控制一下線程的最大并發(fā)數(shù)量
*   盡量把耗時的操作放到子線程

*   文本處理(尺寸計(jì)算、繪制)
*   圖片處理(解碼颂碘、繪制)

卡頓優(yōu)化在 GPU層面

*   盡量避免短時間內(nèi)大量圖片的顯示异赫,盡可能將多張圖片合成一張進(jìn)行顯示
*   GPU能處理的最大紋理尺寸是 4096x4096,一旦超過這個尺寸凭涂,就會占用 CPU 資源進(jìn)行處理,所以紋理盡量不要超過這個尺寸
*   盡量減少視圖數(shù)量和層次
*   減少透明的視圖(alpha<1)贴妻,不透明的就設(shè)置 opaque 為 YES
*   盡量避免出現(xiàn)離屏渲染

[iOS 保持界面流暢的技巧](https://links.jianshu.com/go?to=https%3A%2F%2Fblog.ibireme.com%2F2015%2F11%2F12%2Fsmooth_user_interfaces_for_ios%2F)

1.預(yù)排版切油,提前計(jì)算

在接收到服務(wù)端返回的數(shù)據(jù)后,盡量將 CoreText排版的結(jié)果名惩、單個控件的高度澎胡、cell整體的高度提前計(jì)算好,將其存儲在模型的屬性中。需要使用時攻谁,直接從模型中往外取稚伍,避免了計(jì)算的過程。

盡量少用 UILabel戚宦,可以使用 CALayer个曙。避免使用 AutoLayout的自動布局技術(shù),采取純代碼的方式

2.預(yù)渲染受楼,提前繪制

例如圓形的圖標(biāo)可以提前在垦搬,在接收到網(wǎng)絡(luò)返回?cái)?shù)據(jù)時,在后臺線程進(jìn)行處理艳汽,直接存儲在模型數(shù)據(jù)里猴贰,回到主線程后直接調(diào)用就可以了

避免使用 CALayer 的 Border、corner河狐、shadow米绕、mask 等技術(shù),這些都會觸發(fā)離屏渲染馋艺。

3.異步繪制

4.全局并發(fā)線程

5.高效的圖片異步加載

iOS 性能優(yōu)化面試題(如何優(yōu)化 `APP` 的電量栅干?)

程序的耗電主要在以下四個方面:

*   CPU 處理
*   定位
*   網(wǎng)絡(luò)
*   圖像

優(yōu)化的途徑主要體現(xiàn)在以下幾個方面:

*   盡可能降低 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ù) (XML -> JSON -> ProtoBuf)填大,如果可能建議使用 ProtoBuf。
*   如果請求的返回?cái)?shù)據(jù)相同俏橘,可以使用 NSCache 進(jìn)行緩存
*   使用斷點(diǎn)續(xù)傳允华,避免因網(wǎng)絡(luò)失敗后要重新下載。
*   網(wǎng)絡(luò)不可用的時候寥掐,不嘗試進(jìn)行網(wǎng)絡(luò)請求
*   長時間的網(wǎng)絡(luò)請求靴寂,要提供可以取消的操作
*   采取批量傳輸。下載視頻流的時候召耘,盡量一大塊一大塊的進(jìn)行下載百炬,廣告可以一次下載多個

*   定位層面的優(yōu)化

*   如果只是需要快速確定用戶位置,最好用 CLLocationManager 的 requestLocation 方法污它。定位完成后剖踊,會自動讓定位硬件斷電
*   如果不是導(dǎo)航應(yīng)用庶弃,盡量不要實(shí)時更新位置,定位完畢就關(guān)掉定位服務(wù)
*   盡量降低定位精度德澈,比如盡量不要使用精度最高的 kCLLocationAccuracyBest
*   需要后臺定位時歇攻,盡量設(shè)置 pausesLocationUpdatesAutomatically 為 YES,如果用戶不太可能移動的時候系統(tǒng)會自動暫停位置更新
*   盡量不要使用 startMonitoringSignificantLocationChanges梆造,優(yōu)先考慮 startMonitoringForRegion:

*   硬件檢測優(yōu)化

*   用戶移動缴守、搖晃、傾斜設(shè)備時澳窑,會產(chǎn)生動作(motion)事件斧散,這些事件由加速度計(jì)、陀螺儀摊聋、磁力計(jì)等硬件檢測鸡捐。在不需要檢測的場合,應(yīng)該及時關(guān)閉這些硬件

iOS 性能優(yōu)化面試題(如何有效降低 APP 包的大新椴谩箍镜?)

降低包大小需要從兩方面著手

可執(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)用的代碼

資源包括 圖片、音頻锋拖、視頻 等

*   優(yōu)化的方式可以對資源進(jìn)行無損的壓縮
*   去除沒有用到的資源: [https://github.com/tinymind/LSUnusedResources](https://links.jianshu.com/go?to=https%3A%2F%2Fgithub.com%2Ftinymind%2FLSUnusedResources)

iOS 性能優(yōu)化面試題(什么是 離屏渲染诈悍?什么情況下會觸發(fā)?該如何應(yīng)對兽埃?)

離屏渲染就是在當(dāng)前屏幕緩沖區(qū)以外侥钳,新開辟一個緩沖區(qū)進(jìn)行操作

離屏渲染出發(fā)的場景有以下:

*   圓角 (maskToBounds并用才會觸發(fā))
*   圖層蒙版
*   陰影
*   光柵化

為什么要避免離屏渲染?

CPUGPU在繪制渲染視圖時做了大量的工作柄错。離屏渲染發(fā)生在 GPU層面上舷夺,會創(chuàng)建新的渲染緩沖區(qū),會觸發(fā) OpenGL的多通道渲染管線售貌,圖形上下文的切換會造成額外的開銷给猾,增加 GPU工作量。如果 CPUGPU累計(jì)耗時 16.67毫秒還沒有完成颂跨,就會造成卡頓掉幀敢伸。

圓角屬性毫捣、蒙層遮罩都會觸發(fā)離屏渲染。指定了以上屬性饶辙,標(biāo)記了它在新的圖形上下文中,在未愈合之前斑粱,不可以用于顯示的時候就出發(fā)了離屏渲染则北。

*   在OpenGL中矿微,GPU有2種渲染方式

*   On-Screen Rendering:當(dāng)前屏幕渲染,在當(dāng)前用于顯示的屏幕緩沖區(qū)進(jìn)行渲染操作
*   Off-Screen Rendering:離屏渲染涌矢,在當(dāng)前屏幕緩沖區(qū)以外新開辟一個緩沖區(qū)進(jìn)行渲染操作

*   離屏渲染消耗性能的原因

*   需要創(chuàng)建新的緩沖區(qū)
*   離屏渲染的整個過程娜庇,需要多次切換上下文環(huán)境名秀,先是從當(dāng)前屏幕(On-Screen)切換到離屏(Off-Screen)藕溅;等到離屏渲染結(jié)束以后巾表,將離屏緩沖區(qū)的渲染結(jié)果顯示到屏幕上攒发,又需要將上下文環(huán)境從離屏切換到當(dāng)前屏幕

*   哪些操作會觸發(fā)離屏渲染惠猿?

*   光柵化偶妖,layer.shouldRasterize = YES
*   遮罩,layer.mask
*   圓角趾访,同時設(shè)置 layer.masksToBounds = YES扼鞋、layer.cornerRadius大于0
*   考慮通過 CoreGraphics 繪制裁剪圓角,或者叫美工提供圓角圖片
*   陰影淫半,layer.shadowXXX科吭,如果設(shè)置了 layer.shadowPath 就不會產(chǎn)生離屏渲染

iOS 性能優(yōu)化面試題(如何檢測離屏渲染对人?)

1牺弄、模擬器debug-選中color Offscreen - Renderd離屏渲染的圖層高亮成黃 可能存在性能問題

2猖闪、真機(jī)Instrument-選中Core Animation-勾選Color Offscreen-Rendered Yellow

離屏渲染的觸發(fā)方式

設(shè)置了以下屬性時培慌,都會觸發(fā)離屏繪制:

1吵护、layer.shouldRasterize(光柵化)

光柵化概念:將圖轉(zhuǎn)化為一個個柵格組成的圖象馅而。

光柵化特點(diǎn):每個元素對應(yīng)幀緩沖區(qū)中的一像素瓮恭。

2屯蹦、masks(遮罩)

3登澜、shadows(陰影)

4脑蠕、edge antialiasing(抗鋸齒)

5谴仙、group opacity(不透明)

6晃跺、復(fù)雜形狀設(shè)置圓角等

7哼审、漸變

8涩盾、drawRect

例如我們?nèi)粘探?jīng)常打交道的TableViewCell,因?yàn)門ableViewCell的重繪是很頻繁的(因?yàn)镃ell的復(fù)用),如果Cell的內(nèi)容不斷變化,則Cell需要不斷重繪,如果此時設(shè)置了cell.layer可光柵化春霍。則會造成大量的離屏渲染,降低圖形性能址儒。

如果將不在GPU的當(dāng)前屏幕緩沖區(qū)中進(jìn)行的渲染都稱為離屏渲染莲趣,那么就還有另一種特殊的“離屏渲染”方式:CPU渲染喧伞。如果我們重寫了drawRect方法潘鲫,并且使用任何Core Graphics的技術(shù)進(jìn)行了繪制操作溉仑,就涉及到了CPU渲染浊竟。整個渲染過程由CPU在App內(nèi)同步地完成逐沙,渲染得到的bitmap最后再交由GPU用于顯示吩案。

現(xiàn)在擺在我們面前得有三個選擇:當(dāng)前屏幕渲染徘郭、離屏渲染残揉、CPU渲染抱环,該用哪個呢镇草?這需要根據(jù)具體的使用場景來決定梯啤。

盡量使用當(dāng)前屏幕渲染

鑒于離屏渲染因宇、CPU渲染可能帶來的性能問題察滑,一般情況下贺辰,我們要盡量使用當(dāng)前屏幕渲染魂爪。

離屏渲染 VS CPU渲染

由于GPU的浮點(diǎn)運(yùn)算能力比CPU強(qiáng)滓侍,CPU渲染的效率可能不如離屏渲染撩笆;但如果僅僅是實(shí)現(xiàn)一個簡單的效果夕冲,直接使用CPU渲染的效率又可能比離屏渲染好歹鱼,畢竟離屏渲染要涉及到緩沖區(qū)創(chuàng)建和上下文切換等耗時操作

UIButton 的 masksToBounds = YES又設(shè)置setImage南片、setBackgroundImage疼进、[button setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:@"btn_selected"]]];

下發(fā)生離屏渲染伞广,但是[button setBackgroundColor:[UIColor redColor]];是不會出現(xiàn)離屏渲染的

關(guān)于 UIImageView,現(xiàn)在測試發(fā)現(xiàn)(現(xiàn)版本: iOS10),在性能的范圍之內(nèi),給UIImageView設(shè)置圓角是不會觸發(fā)離屏渲染的,但是同時給UIImageView設(shè)置背景色則肯定會觸發(fā).觸發(fā)離屏渲染跟 png.jpg格式并無關(guān)聯(lián)

日常我們使用layer的兩個屬性嚼锄,實(shí)現(xiàn)圓角

imageView.layer.cornerRaidus = CGFloat(10);

imageView.layer.masksToBounds = YES;

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

iOS 性能優(yōu)化面試題(怎么檢測圖層混合罩驻?)

1惠遏、模擬器debug- 選中 color blended layers紅色區(qū)域表示圖層發(fā)生了混合

2、Instrument-選中Core Animation-勾選Color Blended Layers

避免圖層混合:

1透绩、確泵煜停控件的opaque屬性設(shè)置為true志鞍,確保backgroundColor和父視圖顏色一致且不透明

2统翩、如無特殊需要厂汗,不要設(shè)置低于1的alpha值

3娶桦、確保UIImage沒有alpha通道

UILabel圖層混合解決方法:

iOS8以后設(shè)置背景色為非透明色并且設(shè)置label.layer.masksToBounds=YES讓label只會渲染她的實(shí)際size區(qū)域衷畦,就能解決UILabel的圖層混合問題

iOS8 之前只要設(shè)置背景色為非透明的就行

為什么設(shè)置了背景色但是在iOS8上仍然出現(xiàn)了圖層混合呢?

UILabel在iOS8前后的變化菩混,在iOS8以前沮峡,UILabel使用的是CALayer作為底圖層帖烘,而在iOS8開始,UILabel的底圖層變成了_UILabelLayer乡摹,繪制文本也有所改變聪廉。在背景色的四周多了一圈透明的邊板熊,而這一圈透明的邊明顯超出了圖層的矩形區(qū)域津辩,設(shè)置圖層的masksToBounds為YES時喘沿,圖層將會沿著Bounds進(jìn)行裁剪 圖層混合問題解決了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市窄赋,隨后出現(xiàn)的幾起案子忆绰,更是在濱河造成了極大的恐慌红符,老刑警劉巖预侯,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件双戳,死亡現(xiàn)場離奇詭異飒货,居然都是意外死亡塘辅,警方通過查閱死者的電腦和手機(jī)哲银,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來做院,“玉大人山憨,你說我怎么就攤上這事郁竟。” “怎么了讥蟆?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長质况。 經(jīng)常有香客問我,道長臼朗,這世上最難降的妖魔是什么视哑? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮慷嗜,結(jié)果婚禮上薇溃,老公的妹妹穿的比我還像新娘沐序。我一直安慰自己,他們只是感情好特姐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著捷枯,像睡著了一般淮捆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蚕苇,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天嚼吞,我揣著相機(jī)與錄音,去河邊找鬼舱禽。 笑死炒刁,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的誊稚。 我是一名探鬼主播翔始,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼罗心,長吁一口氣:“原來是場噩夢啊……” “哼城瞎!你這毒婦竟也來了渤闷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤脖镀,失蹤者是張志新(化名)和其女友劉穎飒箭,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蜒灰,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡弦蹂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了强窖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凸椿。...
    茶點(diǎn)故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖翅溺,靈堂內(nèi)的尸體忽然破棺而出削饵,到底是詐尸還是另有隱情,我是刑警寧澤未巫,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布窿撬,位于F島的核電站,受9級特大地震影響叙凡,放射性物質(zhì)發(fā)生泄漏劈伴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一握爷、第九天 我趴在偏房一處隱蔽的房頂上張望跛璧。 院中可真熱鬧,春花似錦新啼、人聲如沸追城。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽座柱。三九已至,卻和暖如春物舒,著一層夾襖步出監(jiān)牢的瞬間色洞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工冠胯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留火诸,地道東北人。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓荠察,卻偏偏與公主長得像置蜀,于是被迫代替她去往敵國和親奈搜。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評論 2 354