iOS 高級面試題--答案

最近準(zhǔn)備復(fù)習(xí)一下面試題虽抄,看到了J_Knight_在18年的出一套 iOS 高級面試題嘗試著回答一下題目走搁,由于水平有限,如有錯誤的地方迈窟,請大家多多指教私植。

目錄

iOS 基礎(chǔ)題

1. 分類和擴展有什么區(qū)別?可以分別用來做什么车酣?分類有哪些局限性曲稼?分類的結(jié)構(gòu)體里面有哪些成員?

2.講一下atomic的實現(xiàn)機制湖员;為什么不能保證絕對的線程安全(最好可以結(jié)合場景來說)贫悄?

3. 被weak修飾的對象在被釋放的時候會發(fā)生什么?是如何實現(xiàn)的破衔?知道sideTable么清女?里面的結(jié)構(gòu)可以畫出來么?

4. 關(guān)聯(lián)對象有什么應(yīng)用晰筛,系統(tǒng)如何管理關(guān)聯(lián)對象嫡丙?其被釋放的時候需要手動將所有的關(guān)聯(lián)對象的指針置空么?

5. KVO的底層實現(xiàn)读第?如何取消系統(tǒng)默認(rèn)的KVO并手動觸發(fā)(給KVO的觸發(fā)設(shè)定條件:改變的值符合某個條件時再觸發(fā)KVO)曙博?

6. Autoreleasepool所使用的數(shù)據(jù)結(jié)構(gòu)是什么?AutoreleasePoolPage結(jié)構(gòu)體了解么怜瞒?

7. 講一下對象父泳,類對象,元類吴汪,跟元類結(jié)構(gòu)體的組成以及他們是如何相關(guān)聯(lián)的惠窄?為什么對象方法沒有保存的對象結(jié)構(gòu)體里,而是保存在類對象的結(jié)構(gòu)體里漾橙?

8. class_ro_t和class_rw_t的區(qū)別杆融?

9. iOS中內(nèi)省的幾個方法?class方法和objc_getClass方法有什么區(qū)別?

10. 在運行時創(chuàng)建類的方法objc_allocateClassPair的方法名尾部為什么是pair(成對的意思)霜运?

11. 一個int變量被__block修飾與否的區(qū)別脾歇?

12. 為什么在block外部使用__weak修飾的同時需要在內(nèi)部使用__strong修飾蒋腮?

13. RunLoop的作用是什么?它的內(nèi)部工作機制了解么藕各?(最好結(jié)合線程和內(nèi)存管理來說)

14. 哪些場景可以觸發(fā)離屏渲染池摧?(知道多少說多少)

iOS實戰(zhàn)題

15. AppDelegate如何瘦身?

16. 反射是什么激况?可以舉出幾個應(yīng)用場景么作彤?(知道多少說多少)

17. 有哪些場景是NSOperation比GCD更容易實現(xiàn)的?(或是NSOperation優(yōu)于GCD的幾點誉碴,知道多少說多少)

18. App 啟動優(yōu)化策略宦棺?最好結(jié)合啟動流程來說(main()函數(shù)的執(zhí)行前后都分別說一下,知道多少說多少)

19. App 無痕埋點的思路了解么黔帕?你認(rèn)為理想的無痕埋點系統(tǒng)應(yīng)該具備哪些特點代咸?(知道多少說多少)

20. 你知道有哪些情況會導(dǎo)致app崩潰,分別可以用什么方法攔截并化解成黄?(知道多少說多少)

21. 你知道有哪些情況會導(dǎo)致app卡頓呐芥,分別可以用什么方法來避免?(知道多少說多少)

網(wǎng)絡(luò)題

22. App 網(wǎng)絡(luò)層有哪些優(yōu)化策略奋岁?

23. TCP為什么要三次握手思瘟,四次揮手?

24. 對稱加密和非對稱加密的區(qū)別闻伶?分別有哪些算法的實現(xiàn)滨攻?

25. HTTPS的握手流程?為什么密鑰的傳遞需要使用非對稱加密蓝翰?雙向認(rèn)證了解么光绕?

26. HTTPS是如何實現(xiàn)驗證身份和驗證完整性的?

27. 如何用Charles抓HTTPS的包畜份?其中原理和流程是什么诞帐?

28. 什么是中間人攻擊?如何避免爆雹?

計算機系統(tǒng)題

29. 了解編譯的過程么停蕉?分為哪幾個步驟?

30. 靜態(tài)鏈接了解么钙态?靜態(tài)庫和動態(tài)庫的區(qū)別慧起?

31. 內(nèi)存的幾大區(qū)域,各自的職能分別是什么册倒?

32. static和const有什么區(qū)別完慧?

33. 了解內(nèi)聯(lián)函數(shù)么?

34. 什么時候會出現(xiàn)死鎖?如何避免屈尼?

35. 說一說你對線程安全的理解?

36. 列舉你知道的線程同步策略拴孤?

37. 有哪幾種鎖脾歧?各自的原理?它們之間的區(qū)別是什么演熟?最好可以結(jié)合使用場景來說

設(shè)計模式題和架構(gòu) & 設(shè)計題(由于水平有限鞭执,就不誤人子弟了)

數(shù)據(jù)結(jié)構(gòu)&算法題

38. 鏈表和數(shù)組的區(qū)別是什么?插入和查詢的時間復(fù)雜度分別是多少芒粹?

39. 哈希表是如何實現(xiàn)的兄纺?如何解決地址沖突?

40. 排序題:冒泡排序化漆,選擇排序估脆,插入排序,快速排序(二路座云,三路)能寫出那些疙赠?

41. 鏈表題:如何檢測鏈表中是否有環(huán)?如何刪除鏈表中等于某個值的所有節(jié)點朦拖?

42. 數(shù)組題:如何在有序數(shù)組中找出和等于給定值的兩個元素圃阳?如何合并兩個有序的數(shù)組之后保持有序?

43. 二叉樹題:如何反轉(zhuǎn)二叉樹璧帝?如何驗證兩個二叉樹是完全相等的捍岳?

1.分類和擴展有什么區(qū)別?可以分別用來做什么睬隶?分類有哪些局限性锣夹?分類的結(jié)構(gòu)體里面有哪些成員?<span id="1.1"></span>

  1. 分類主要用來為某個類添加方法理疙,屬性晕城,協(xié)議(我一般用來為系統(tǒng)的類擴展方法或者把某個復(fù)雜的類的按照功能拆到不同的文件里)
  2. 擴展主要用來為某個類原來沒有的成員變量、屬性窖贤、方法砖顷。注:方法只是聲明(我一般用擴展來聲明私有屬性,或者把.h的只讀屬性重寫成可讀寫的)

分類和擴展的區(qū)別:

  1. 分類是在運行時把分類信息合并到類信息中赃梧,而擴展是在編譯時滤蝠,就把信息合并到類中的
  2. 分類聲明的屬性,只會生成getter/setter方法的聲明授嘀,不會自動生成成員變量和getter/setter方法的實現(xiàn)物咳,而擴展會
  3. 分類不可用為類添加實例變量,而擴展可以
  4. 分類可以為類添加方法的實現(xiàn)蹄皱,而擴展只能聲明方法览闰,而不能實現(xiàn)

分類的局限性:

  1. 無法為類添加實例變量芯肤,但可通過關(guān)聯(lián)對象進行實現(xiàn),注:關(guān)聯(lián)對象中內(nèi)存管理沒有weak压鉴,用時需要注意野指針的問題崖咨,可通過其他辦法來實現(xiàn),具體可參考iOS weak 關(guān)鍵字漫談
  2. 分類的方法若和類中原本的實現(xiàn)重名油吭,會覆蓋原本方法的實現(xiàn)击蹲,注:并不是真正的覆蓋
  3. 多個分類的方法重名,會調(diào)用最后編譯的那個分類的實現(xiàn)

分類的結(jié)構(gòu)體里有哪些成員

struct category_t {
    const char *name; //名字
    classref_t cls; //類的引用
    struct method_list_t *instanceMethods;//實例方法列表
    struct method_list_t *classMethods;//類方法列表
    struct protocol_list_t *protocols;//協(xié)議列表
    struct property_list_t *instanceProperties;//實例屬性列表
    // 此屬性不一定真正的存在
    struct property_list_t *_classProperties;//類屬性列表
};

2.講一下atomic的實現(xiàn)機制婉宰;為什么不能保證絕對的線程安全(最好可以結(jié)合場景來說)歌豺?<span id="1.2"></span>

  1. atomic的實現(xiàn)機制
    atomic是property的修飾詞之一,表示是原子性的心包,使用方式為@property(atomic)int age;,此時編譯器會自動生成getter/setter方法类咧,最終會調(diào)用objc_getPropertyobjc_setProperty方法來進行存取屬性绑嘹。若此時屬性用atomic修飾的話糠聪,在這兩個方法內(nèi)部使用os_unfair_lock來進行加鎖齐佳,來保證讀寫的原子性轨蛤。鎖都在PropertyLocks中保存著(在iOS平臺會初始化8個抡诞,mac平臺64個)淹父,在用之前萧落,會把鎖都初始化好端逼,在需要用到時珊随,用對象的地址加上成員變量的偏移量為key述寡,去PropertyLocks中去取。因此存取時用的是同一個鎖叶洞,所以atomic能保證屬性的存取時是線程安全的鲫凶。注:由于鎖是有限的,不用對象衩辟,不同屬性的讀取用的也可能是同一個鎖
  2. atomic為什么不能保證絕對的線程安全螟炫?
    1. atomic在getter/setter方法中加鎖,僅保證了存取時的線程安全艺晴,假設(shè)我們的屬性是@property(atomic)NSMutableArray *array;可變的容器時,無法保證對容器的修改是線程安全的
    2. 在編譯器自動生產(chǎn)的getter/setter方法昼钻,最終會調(diào)用objc_getPropertyobjc_setProperty方法存取屬性,在此方法內(nèi)部保證了讀寫時的線程安全的封寞,當(dāng)我們重寫getter/setter方法時然评,就只能依靠自己在getter/setter中保證線程安全

3. 被weak修飾的對象在被釋放的時候會發(fā)生什么?是如何實現(xiàn)的狈究?知道sideTable么碗淌?里面的結(jié)構(gòu)可以畫出來么?<span id="1.3"></span>

  1. 被weak修飾的對象在被釋放的時候會發(fā)生什么?
    被weak修飾的對象在被釋放的時候亿眠,會把weak指針自動置位nil

  2. weak是如何實現(xiàn)的碎罚?
    runTime會把對weak修飾的對象放到一個全局的哈希表中,用weak修飾的對象的內(nèi)存地址為key缕探,weak指針為值魂莫,在對象進行銷毀時,用通過自身地址去哈希表中查找到所有指向此對象的weak指針爹耗,并把所有的weak指針置位nil

  3. sideTable的結(jié)構(gòu)

struct SideTable {
    spinlock_t slock;//操作SideTable時用到的鎖
    RefcountMap refcnts;//引用計數(shù)器的值
    weak_table_t weak_table;//存放weak指針的哈希表
};

4. 關(guān)聯(lián)對象有什么應(yīng)用,系統(tǒng)如何管理關(guān)聯(lián)對象谜喊?其被釋放的時候需要手動將所有的關(guān)聯(lián)對象的指針置空么潭兽?<span id="1.4"></span>

  1. 關(guān)聯(lián)對象有什么應(yīng)用?
    一般用于在分類中給類添加實例變量
  2. 系統(tǒng)如何管理關(guān)聯(lián)對象斗遏?
    首先系統(tǒng)中有一個全局AssociationsManager,里面有個AssociationsHashMap哈希表山卦,哈希表中的key是對象的內(nèi)存地址,value是ObjectAssociationMap,也是一個哈希表诵次,其中key是我們設(shè)置關(guān)聯(lián)對象所設(shè)置的key账蓉,value是ObjcAssociation,里面存放著關(guān)聯(lián)對象設(shè)置的值和內(nèi)存管理的策略。
    void objc_setAssociatedObject(id object, const void * key,id value, objc_AssociationPolicy policy)為例逾一,首先會通過AssociationsManager獲取AssociationsHashMap铸本,然后以object的內(nèi)存地址為key,從AssociationsHashMap中取出ObjectAssociationMap遵堵,若沒有箱玷,則新創(chuàng)建一個ObjectAssociationMap,然后通過key獲取舊值陌宿,以及通過key和policy生成新值ObjcAssociation(policy, new_value)锡足,把新值存放到ObjectAssociationMap中,若新值不為nil壳坪,并且內(nèi)存管理策略為retain舶得,則會對新值進行一次retain,若新值為nil爽蝴,則會刪除舊值沐批,若舊值不為空并且內(nèi)存管理的策略是retain,則對舊值進行一次release
  3. 其被釋放的時候需要手動將所有的關(guān)聯(lián)對象的指針置空么霜瘪?
    注:對這個問題我的理解是:當(dāng)對象被釋放時珠插,需要手動移除該對象所設(shè)置的關(guān)聯(lián)對象嗎?
    不需要颖对,因為在對象的dealloc中捻撑,若發(fā)現(xiàn)對象有關(guān)聯(lián)對象時,會調(diào)用_object_remove_assocations方法來移除所有的關(guān)聯(lián)對象,并根據(jù)內(nèi)存策略顾患,來判斷是否需要對關(guān)聯(lián)對象的值進行release

5. KVO的底層實現(xiàn)番捂?如何取消系統(tǒng)默認(rèn)的KVO并手動觸發(fā)(給KVO的觸發(fā)設(shè)定條件:改變的值符合某個條件時再觸發(fā)KVO)?<span id="1.5"></span>

  1. KVO的底層實現(xiàn)江解?

    1. 當(dāng)某個類的屬性被觀察時设预,系統(tǒng)會在運行時動態(tài)的創(chuàng)建一個該類的子類。并且把改對象的isa指向這個子類

    2. 假設(shè)被觀察的屬性名是name犁河,若父類里有setName:或這_setName:,那么在子類里重寫這2個方法鳖枕,若2個方法同時存在,則只會重寫setName:一個(這里和KVCset時的搜索順序是一樣的)

    3. 若被觀察的類型是NSString,那么重寫的方法的實現(xiàn)會指向_NSSetObjectValueAndNotify這個函數(shù)桨螺,若是Bool類型宾符,那么重寫的方法的實現(xiàn)會指向_NSSetBoolValueAndNotify這個函數(shù),這個函數(shù)里會調(diào)用willChangeValueForKey:didChangevlueForKey:,并且會在這2個方法調(diào)用之間灭翔,調(diào)用父類set方法的實現(xiàn)

    4. 系統(tǒng)會在willChangeValueForKey:對observe里的change[old]賦值魏烫,取值是用valueForKey:取值的,didChangevlueForKey:對observe里的change[new]賦值,然后調(diào)用observe的這個方法- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context;

    5. 當(dāng)使用KVC賦值的時候,在NSObject里的setValue:forKey:方法里,若父類不存在setName:或這_setName:這些方法,會調(diào)用_NSSetValueAndNotifyForKeyInIvar這個函數(shù)肝箱,這個函數(shù)里同樣也會調(diào)用willChangeValueForKey:didChangevlueForKey:,若存在則調(diào)用

  2. 如何取消系統(tǒng)默認(rèn)的KVO并手動觸發(fā)(給KVO的觸發(fā)設(shè)定條件:改變的值符合某個條件時再觸發(fā)KVO)哄褒?
    舉例:取消Person類age屬性的默認(rèn)KVO,設(shè)置age大于18時煌张,手動觸發(fā)KVO

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
    if ([key isEqualToString:@"age"]) {
        return NO;
    }
    return [super automaticallyNotifiesObserversForKey:key];
}

- (void)setAge:(NSInteger)age {
    if (age > 18 ) {
        [self willChangeValueForKey:@"age"];
        _age = age;
        [self didChangeValueForKey:@"age"];
    }else {
        _age = age;
    }
}

6. Autoreleasepool所使用的數(shù)據(jù)結(jié)構(gòu)是什么呐赡?AutoreleasePoolPage結(jié)構(gòu)體了解么?<span id="1.6"></span>

Autoreleasepool是由多個AutoreleasePoolPage以雙向鏈表的形式連接起來的唱矛,
Autoreleasepool的基本原理:在每個自動釋放池創(chuàng)建的時候罚舱,會在當(dāng)前的AutoreleasePoolPage中設(shè)置一個標(biāo)記位,在此期間绎谦,當(dāng)有對象調(diào)用autorelsease時管闷,會把對象添加到AutoreleasePoolPage中,若當(dāng)前頁添加滿了窃肠,會初始化一個新頁包个,然后用雙向量表鏈接起來,并把新初始化的這一頁設(shè)置為hotPage,當(dāng)自動釋放池pop時冤留,從最下面依次往上pop碧囊,調(diào)用每個對象的release方法,直到遇到標(biāo)志位纤怒。
AutoreleasePoolPage結(jié)構(gòu)如下

class AutoreleasePoolPage {
    magic_t const magic;
    id *next;//下一個存放autorelease對象的地址
    pthread_t const thread; //AutoreleasePoolPage 所在的線程
    AutoreleasePoolPage * const parent;//父節(jié)點
    AutoreleasePoolPage *child;//子節(jié)點
    uint32_t const depth;//深度,也可以理解為當(dāng)前page在鏈表中的位置
    uint32_t hiwat;
}

7. 講一下對象糯而,類對象,元類泊窘,跟元類結(jié)構(gòu)體的組成以及他們是如何相關(guān)聯(lián)的熄驼?為什么對象方法沒有保存的對象結(jié)構(gòu)體里像寒,而是保存在類對象的結(jié)構(gòu)體里?<span id="1.7"></span>

  1. 講一下對象瓜贾,類對象诺祸,元類,跟元類結(jié)構(gòu)體的組成以及他們是如何相關(guān)聯(lián)的祭芦?
    對象的結(jié)構(gòu)體里存放著isa和成員變量筷笨,isa指向類對象。
    類對象的isa指向元類龟劲,元類的isa指向NSObject的元類胃夏。
    類對象和元類的結(jié)構(gòu)體有isa、superclass昌跌、cache构订、bits,bits里存放著class_rw_t的指針避矢。
    放一張經(jīng)典的圖
    isa
  2. 為什么對象方法沒有保存的對象結(jié)構(gòu)體里,而是保存在類對象的結(jié)構(gòu)體里囊榜?
    方法是每個對象互相可以共用的审胸,如果每個對象都存儲一份方法列表太浪費內(nèi)存,由于對象的isa是指向類對象的卸勺,當(dāng)調(diào)用的時候砂沛,直接去類對象中查找就行了∈锴螅可以節(jié)約很多內(nèi)存空間的

8. class_ro_t和class_rw_t的區(qū)別碍庵?

class_rw_t提供了運行時對類拓展的能力,而class_ro_t存儲的大多是類在編譯時就已經(jīng)確定的信息悟狱。二者都存有類的方法静浴、屬性(成員變量)、協(xié)議等信息挤渐,不過存儲它們的列表實現(xiàn)方式不同苹享。簡單的說class_rw_t存儲列表使用的二維數(shù)組,class_ro_t使用的一維數(shù)組浴麻。
class_ro_t存儲于class_rw_t結(jié)構(gòu)體中得问,是不可改變的。保存著類的在編譯時就已經(jīng)確定的信息软免。而運行時修改類的方法宫纬,屬性,協(xié)議等都存儲于class_rw_t中

9. iOS中內(nèi)省的幾個方法膏萧?class方法和objc_getClass方法有什么區(qū)別?

  1. 什么是內(nèi)世焐А蝌衔?

在計算機科學(xué)中,內(nèi)省是指計算機程序在運行時(Run time)檢查對象(Object)類型的一種能力认境,通常也可以稱作運行時類型檢查胚委。
不應(yīng)該將內(nèi)省和反射混淆。相對于內(nèi)省叉信,反射更進一步亩冬,是指計算機程序在運行時(Run time)可以訪問、檢測和修改它本身狀態(tài)或行為的一種能力硼身。

  1. iOS中內(nèi)省的幾個方法硅急?

    • isMemberOfClass //對象是否是某個類型的對象
    • isKindOfClass //對象是否是某個類型或某個類型子類的對象
    • isSubclassOfClass //某個類對象是否是另一個類型的子類
    • isAncestorOfObject //某個類對象是否是另一個類型的父類
    • respondsToSelector //是否能響應(yīng)某個方法
    • conformsToProtocol //是否遵循某個協(xié)議
  2. class方法和object_getClass方法有什么區(qū)別?
    實例class方法就直接返回object_getClass(self),類class方法直接返回self,而object_getClass(類對象)佳遂,則返回的是元類

10. 在運行時創(chuàng)建類的方法objc_allocateClassPair的方法名尾部為什么是pair(成對的意思)营袜?

因為此方法會創(chuàng)建一個類對象以及元類,正好組成一隊

Class objc_allocateClassPair(Class superclass, const char *name, 
                             size_t extraBytes){
    ...省略了部分代碼
    //生成一個類對象
    cls  = alloc_class_for_subclass(superclass, extraBytes);
    //生成一個類對象元類對象
    meta = alloc_class_for_subclass(superclass, extraBytes);
    objc_initializeClassPair_internal(superclass, name, cls, meta);
    return cls;
}

11. 一個int變量被__block修飾與否的區(qū)別丑罪?

int變量被__block修飾之后會被包裝成一個對象,如__block int age會被包裝成下面這樣

struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding; //指向自己
 int __flags;
 int __size;
 int age;//包裝的具體的值
};
// age = 20;會被編譯成下面這樣
(age.__forwarding->age) = 20;

12. 為什么在block外部使用__weak修飾的同時需要在內(nèi)部使用__strong修飾荚板?

用__weak修飾之后block不會對該對象進行retain,只是持有了weak指針吩屹,在block執(zhí)行之前或執(zhí)行的過程時跪另,隨時都有可能被釋放,將weak指針置位nil煤搜,產(chǎn)生一些未知的錯誤免绿。在內(nèi)部用__strong修飾,會在block執(zhí)行時擦盾,對該對象進行一次retain嘲驾,保證在執(zhí)行時若該指針不指向nil,則在執(zhí)行過程中不會指向nil迹卢。但有可能在執(zhí)行執(zhí)行之前已經(jīng)為nil了

13. RunLoop的作用是什么辽故?它的內(nèi)部工作機制了解么?(最好結(jié)合線程和內(nèi)存管理來說)

  1. 什么是RunLoop
    一般來講婶希,一個線程一次只能執(zhí)行一個任務(wù)榕暇,執(zhí)行完成后線程就會退出。如果我們需要一個機制喻杈,讓線程能隨時處理事件但并不退出彤枢。這種模型通常被稱作 Event Loop。 Event Loop 在很多系統(tǒng)和框架里都有實現(xiàn)筒饰,比如 Node.js 的事件處理缴啡,比如 Windows 程序的消息循環(huán),再比如 OSX/iOS 里的 RunLoop瓷们。實現(xiàn)這種模型的關(guān)鍵點在于:如何管理事件/消息业栅,如何讓線程在沒有處理消息時休眠以避免資源占用秒咐、在有消息到來時立刻被喚醒。
  2. RunLoop的作用是什么碘裕?(由于水平有限携取,不是很理解作者的本意,我對題目的理解是帮孔,利用RunLoop可以做哪些事情雷滋?)
    1. 保持程序的持續(xù)運行,在iOS線程中文兢,會在main方法給主線程創(chuàng)建一個RunLoop晤斩,保證主線程不被銷毀
    2. 處理APP中的各種事件(如touch,timer姆坚,performSelector等)
    3. 界面更新
    4. 手勢識別
    5. AutoreleasePool
      1. 系統(tǒng)在主線程RunLoop注冊了2個observer
      2. 第一個observe監(jiān)聽即將進入RunLoop澳泵,調(diào)用_objc_autoreleasePoolPush()創(chuàng)建自動釋放池
      3. 第二個observe監(jiān)聽兩個事件,進入休眠之前和即將退出RunLoop
      4. 在進入休眠之前的回調(diào)里兼呵,會先釋放自動釋放池兔辅,然后在創(chuàng)建一個自動釋放池
      5. 在即將退出的回調(diào)里,會釋放自動釋放池
    6. 線程被魑梗活
    7. 監(jiān)測卡頓
  3. RunLoop的內(nèi)部邏輯(下圖來自于YY大神的博客)
RunLoop_1

14. 哪些場景可以觸發(fā)離屏渲染幢妄?(知道多少說多少)

  1. 添加遮罩mask
  2. 添加陰影shadow
  3. 設(shè)置圓角并且設(shè)置masksToBounds為true
  4. 設(shè)置allowsGroupOpacity為true并且layer.opacity小于1.0和有子layer或者背景不為空
  5. 開啟光柵化shouldRasterize=true

15.AppDelegate如何瘦身?<span id="15"></span>

  1. AppDelegate為什么會那么臃腫茫负?
    AppDelegate是一個項目的入口,承擔(dān)了太多的功能乎赴,如初始化根控制器忍法,管理應(yīng)用的狀態(tài),管理推送榕吼,和其他APP交互饿序,初始化第三方SDK,獲取權(quán)限等等
  2. 如何瘦身
    瘦身的方案有很多羹蚣,比如說把某些方法放到swift擴展或者OC的分類中原探,抽取中間類,利用通知監(jiān)聽等等顽素,不過我比較喜歡的是使用命令設(shè)計模式進行瘦身咽弦。
    命令模式是描述對象被稱作命令相當(dāng)于是一個簡單的方法或者事件。因為對象封裝了觸發(fā)自身所需的所有參數(shù)胁出,因此命令的調(diào)用者不知道該命令做了什么以及響應(yīng)者是誰
    可以為APPdelegate的每一個職責(zé)定義一個命令型型,這個命令的名字有他們自己指定
protocol Command {
    func execute()
}

struct InitializeThirdPartiesCommand: Command {
    func execute() {
        // 初始化第三方庫
    }
}
struct InitialViewControllerCommand: Command {
    let keyWindow: UIWindow
    func execute() {
        // 設(shè)置根控制器
        keyWindow.rootViewController = UIViewController()
    }
}

struct RegisterToRemoteNotificationsCommand: Command {
    func execute() {
        // 注冊遠(yuǎn)程推送
    }
}

然后我們定義StartupCommandsBuilder來封裝如何創(chuàng)建命令的詳細(xì)信息。APPdelegate調(diào)用這個builder去初始化命令并執(zhí)行這些命令

// MARK: - Builder
final class StartupCommandsBuilder {
    private var window: UIWindow!
    func setKeyWindow(_ window: UIWindow) -> StartupCommandsBuilder {
        self.window = window
        return self
    }
    func build() -> [Command] {
        return [
            InitializeThirdPartiesCommand(),
            InitialViewControllerCommand(keyWindow: window),
            RegisterToRemoteNotificationsCommand()
        ]
    }
}
// MARK: - App Delegate
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        StartupCommandsBuilder()
            .setKeyWindow(window!)
            .build()
            .forEach { $0.execute() }

        return true
    }
}

如果APPdelegate需要添加新的職責(zé)全蝶,則可以創(chuàng)建新的命令闹蒜,然后把命令添加到builder里去而無需去改變APPdelegate寺枉。而且使用命令模式有以下好處

  • 每個命令都有單一的職責(zé)
  • 無需更改APPdelegate就可以很容易的添加新的命令
  • 每個命令可以很容易的被單獨測試

16. 反射是什么?可以舉出幾個應(yīng)用場景么绷落?(知道多少說多少)<span id="16"></span>

  1. 什么是反射

反射是指計算機程序在運行時(runtime)可以訪問姥闪、檢測和修改它本身狀態(tài)或行為的一種能力。用比喻來說砌烁,反射就是程序在運行的時候能夠“觀察”并且修改自己的行為筐喳。

在OC中,反射是指程序在運行時往弓,獲取和修改類的信息疏唾。

  1. 應(yīng)用場景
    • JSON與模型之間的相互轉(zhuǎn)換
    • Method Swizzling
    • KVO的實現(xiàn)原理
    • 實現(xiàn)NSCoding的自動歸檔和自動解檔
    • 探索系統(tǒng)某些類的具體實現(xiàn)

17. 有哪些場景是NSOperation比GCD更容易實現(xiàn)的?(或是NSOperation優(yōu)于GCD的幾點函似,知道多少說多少)<span id="17"></span>

  1. NSOperation可以設(shè)置依賴
  2. NSOperation可以進行暫停槐脏,繼續(xù)等操作
  3. NSOperation可以監(jiān)測當(dāng)前隊列運行的狀態(tài)
  4. NSOperationQueue可以取消隊列里的所有操作
  5. NSOperationQueue很方便的設(shè)置最大并發(fā)數(shù)

18. App啟動優(yōu)化策略?最好結(jié)合啟動流程來說(main()函數(shù)的執(zhí)行前后都分別說一下撇寞,知道多少說多少)<span id="18"></span>

  1. iOS的啟動流程
    1. 根據(jù) info.plist 里的設(shè)置加載閃屏顿天,建立沙箱,對權(quán)限進行檢查等
    2. 加載可執(zhí)行文件
    3. 加載動態(tài)鏈接庫蔑担,進行 rebase 指針調(diào)整和 bind 符號綁定
    4. Objc 運行時的初始處理牌废,包括 Objc 相關(guān)類的注冊、category 注冊啤握、selector 唯一性檢查等鸟缕;
    5. 初始化,包括了執(zhí)行 +load() 方法排抬、attribute((constructor)) 修飾的函數(shù)的調(diào)用懂从、創(chuàng)建 C++ 靜態(tài)全局變量。
    6. 執(zhí)行 main 函數(shù)
    7. Application 初始化蹲蒲,到 applicationDidFinishLaunchingWithOptions 執(zhí)行完
    8. 初始化幀渲染番甩,到 viewDidAppear 執(zhí)行完,用戶可見可操作届搁。
  2. 啟動優(yōu)化
    1. 減少動態(tài)庫的加載
    2. 去除掉無用的類和C++全局變量的數(shù)量
    3. 盡量讓load方法中的內(nèi)容放到首屏渲染之后再去執(zhí)行缘薛,或者使用initialize替換
    4. 去除在首屏展現(xiàn)之前非必要的功能
    5. 檢查首屏展現(xiàn)之前主線程的耗時方法,將沒必要的耗時方法滯后或者延遲執(zhí)行

19. App無痕埋點的思路了解么卡睦?你認(rèn)為理想的無痕埋點系統(tǒng)應(yīng)該具備哪些特點宴胧?(知道多少說多少))<span id="19"></span>

App無痕埋點的思路是利用AOP來攔截用戶的操作并進行標(biāo)記記錄然后進行上傳

我認(rèn)為理想的無痕埋點系統(tǒng)應(yīng)該具備以下特點

  1. 不侵入業(yè)務(wù)代碼
  2. 統(tǒng)計盡可能多的事件
  3. 自動生成唯一標(biāo)識
  4. 要能統(tǒng)計到控件在但不同狀態(tài)意義不同的情況
  5. 需要某些機制能夠提供業(yè)務(wù)數(shù)據(jù)
  6. 在合適的時機去上傳數(shù)據(jù)

20. 你知道有哪些情況會導(dǎo)致app崩潰,分別可以用什么方法攔截并化解表锻?(知道多少說多少))<span id="20"></span>

  1. unrecognized selector sent to instance 方法找不到
  2. 數(shù)組越界牺汤,插入空值
  3. [NSDictionary initWithObjects:forKeys:]使用此方法初始化字典時,objects和keys的數(shù)量不一致時
  4. NSMutableDictionary浩嫌,setObject:forKey:或者removeObjectForKey:時檐迟,key為nil
  5. setValue:forUndefinedKey:补胚,使用KVC對對象進行存取值時傳入錯誤的key或者對不可變字典進行賦值
  6. NSUserDefaults 存儲時key為nil
  7. 對字符串操作時,傳遞的下標(biāo)超出范圍追迟,判斷是否存在前綴溶其,后綴子串時,子串為空
  8. 使用C字符串初始化字符串時敦间,傳入null
  9. 對可變集合或字符串使用copy修飾并進行修改操作
  10. 在空間未添加到父元素上之前瓶逃,就使用autoLayout進行布局
  11. KVO在對象銷毀時,沒有移除KVO或者多次移除KVO
  12. 野指針訪問
  13. 死鎖
  14. 除0

1-9都可以利用Runtime進行攔截廓块,然后進行一些邏輯處理厢绝,防止crash

21. 你知道有哪些情況會導(dǎo)致app卡頓,分別可以用什么方法來避免带猴?(知道多少說多少))<span id="21"></span>

  1. 主線程中進化IO或其他耗時操作昔汉,解決:把耗時操作放到子線程中操作
  2. GCD并發(fā)隊列短時間內(nèi)創(chuàng)建大量任務(wù),解決:使用線程池
  3. 文本計算拴清,解決:把計算放在子線程中避免阻塞主線程
  4. 大量圖像的繪制靶病,解決:在子線程中對圖片進行解碼之后再展示
  5. 高清圖片的展示,解法:可在子線程中進行下采樣處理之后再展示

22. App網(wǎng)絡(luò)層有哪些優(yōu)化策略口予?<span id="22"></span>

  1. 優(yōu)化DNS解析和緩存
  2. 對傳輸?shù)臄?shù)據(jù)進行壓縮娄周,減少傳輸?shù)臄?shù)據(jù)
  3. 使用緩存手段減少請求的發(fā)起次數(shù)
  4. 使用策略來減少請求的發(fā)起次數(shù),比如在上一個請求未著地之前沪停,不進行新的請求
  5. 避免網(wǎng)絡(luò)抖動煤辨,提供重發(fā)機制

23. TCP為什么要三次握手,四次揮手木张?<span id="23"></span>

三次握手:

  1. 客戶端向服務(wù)端發(fā)起請求鏈接掷酗,首先發(fā)送SYN報文,SYN=1窟哺,seq=x,并且客戶端進入SYN_SENT狀態(tài)
  2. 服務(wù)端收到請求鏈接,服務(wù)端向客戶端進行回復(fù)技肩,并發(fā)送響應(yīng)報文且轨,SYN=1,seq=y,ACK=1,ack=x+1,并且服務(wù)端進入到SYN_RCVD狀態(tài)
  3. 客戶端收到確認(rèn)報文后虚婿,向服務(wù)端發(fā)送確認(rèn)報文旋奢,ACK=1,ack=y+1然痊,此時客戶端進入到ESTABLISHED至朗,服務(wù)端收到用戶端發(fā)送過來的確認(rèn)報文后,也進入到ESTABLISHED狀態(tài)剧浸,此時鏈接創(chuàng)建成功

四次揮手:

  1. 客戶端向服務(wù)端發(fā)起關(guān)閉鏈接锹引,并停止發(fā)送數(shù)據(jù)
  2. 服務(wù)端收到關(guān)閉鏈接的請求時矗钟,向客戶端發(fā)送回應(yīng),我知道了嫌变,然后停止接收數(shù)據(jù)
  3. 當(dāng)服務(wù)端發(fā)送數(shù)據(jù)結(jié)束之后吨艇,向客戶端發(fā)起關(guān)閉鏈接,并停止發(fā)送數(shù)據(jù)
  4. 客戶端收到關(guān)閉鏈接的請求時腾啥,向服務(wù)端發(fā)送回應(yīng)东涡,我知道了,然后停止接收數(shù)據(jù)

為什么需要三次握手:
為了防止已失效的連接請求報文段突然又傳送到了服務(wù)端倘待,因而產(chǎn)生錯誤疮跑,假設(shè)這是一個早已失效的報文段。但server收到此失效的連接請求報文段后凸舵,就誤認(rèn)為是client再次發(fā)出的一個新的連接請求祖娘。于是就向client發(fā)出確認(rèn)報文段,同意建立連接贞间。假設(shè)不采用“三次握手”贿条,那么只要server發(fā)出確認(rèn),新的連接就建立了增热。由于現(xiàn)在client并沒有發(fā)出建立連接的請求整以,因此不會理睬server的確認(rèn),也不會向server發(fā)送數(shù)據(jù)峻仇。但server卻以為新的運輸連接已經(jīng)建立公黑,并一直等待client發(fā)來數(shù)據(jù)。這樣摄咆,server的很多資源就白白浪費掉了凡蚜。

為什么需要四次揮手:
因為TCP是全雙工通信的,在接收到客戶端的關(guān)閉請求時吭从,還可能在向客戶端發(fā)送著數(shù)據(jù)朝蜘,因此不能再回應(yīng)關(guān)閉鏈接的請求時,同時發(fā)送關(guān)閉鏈接的請求

24. 對稱加密和非對稱加密的區(qū)別涩金?分別有哪些算法的實現(xiàn)谱醇?<span id="24"></span>

對稱加密,加密的加密和解密使用同一密鑰步做。

非對稱加密副渴,使用一對密鑰用于加密和解密,分別為公開密鑰和私有密鑰全度。公開密鑰所有人都可以獲得煮剧,通信發(fā)送方獲得接收方的公開密鑰之后,就可以使用公開密鑰進行加密,接收方收到通信內(nèi)容后使用私有密鑰解密勉盅。

對稱加密常用的算法實現(xiàn)有AES,ChaCha20,DES,不過DES被認(rèn)為是不安全的;非對稱加密用的算法實現(xiàn)有RSA佑颇,ECC

25. HTTPS的握手流程?為什么密鑰的傳遞需要使用非對稱加密菇篡?雙向認(rèn)證了解么漩符?<span id="25"></span>

HTTPS的握手流程,如下圖驱还,摘自圖解HTTP

image
  1. 客戶端發(fā)送Client Hello 報文開始SSL通信嗜暴。報文中包含客戶端支持的SSL的版本,加密組件列表议蟆。
  2. 服務(wù)器收到之后闷沥,會以Server Hello 報文作為應(yīng)答。和客戶端一樣咐容,報文中包含客戶端支持的SSL的版本舆逃,加密組件列表。服務(wù)器的加密組件內(nèi)容是從接收到的客戶端加密組件內(nèi)篩選出來的
  3. 服務(wù)器發(fā)送Certificate報文戳粒。報文中包含公開密鑰證書路狮。
  4. 然后服務(wù)器發(fā)送Server Hello Done報文通知客戶端,最初階段的SSL握手協(xié)商部分結(jié)束
  5. SSL第一次握手結(jié)束之后蔚约,客戶端以Client Key Exchange報文作為會議奄妨。報文中包含通信加密中使用的一種被稱為Pre-master secret的隨機密碼串
  6. 接著客戶端發(fā)送Change Cipher Space報文。該報文會提示服務(wù)器苹祟,在次報文之后的通信會采用Pre-master secret密鑰加密
  7. 客戶端發(fā)送Finished 報文砸抛。該報文包含鏈接至今全部報文的整體校驗值。這次握手協(xié)商是否能夠成功树枫,要以服務(wù)器是否能夠正確揭秘該報文作為判定標(biāo)準(zhǔn)
  8. 服務(wù)器同樣發(fā)送Change Cipher Space報文直焙。
  9. 服務(wù)器同樣發(fā)送Finished報文。
  10. 服務(wù)器和客戶端的Finished報文交換完畢之后砂轻,SSL連接建立完成奔誓,從此開始HTTP通信,通信的內(nèi)容都使用Pre-master secret加密搔涝。然后開始發(fā)送HTTP請求
  11. 應(yīng)用層收到HTTP請求之后汗盘,發(fā)送HTTP響應(yīng)
  12. 最后有客戶端斷開連接

為什么密鑰的傳遞需要使用非對稱加密改执?

答:使用非對稱加密是為了后面客戶端生成的Pre-master secret密鑰的安全校辩,通過上面的步驟能得知翻默,服務(wù)器向客戶端發(fā)送公鑰證書這一步是有可能被別人攔截的臼婆,如果使用對稱加密的話抒痒,在客戶端向服務(wù)端發(fā)送Pre-master secret密鑰的時候,被黑客攔截的話颁褂,就能夠使用公鑰進行解碼故响,就無法保證Pre-master secret密鑰的安全了

雙向認(rèn)證了解么傀广?(這里我真想說一句不了解)

答:上面的HTTPS的通信流程只驗證了服務(wù)端的身份,而服務(wù)端沒有驗證客戶端的身份彩届,雙向認(rèn)證是服務(wù)端也要確蔽北客戶端的身份,大概流程是客戶端在校驗完服務(wù)器的證書之后樟蠕,會向服務(wù)器發(fā)送自己的公鑰贮聂,然后服務(wù)端用公鑰加密產(chǎn)生一個新的密鑰,傳給客戶端寨辩,客戶端再用私鑰解密吓懈,以后就用此密鑰進行對稱加密的通信

26. HTTPS是如何實現(xiàn)驗證身份和驗證完整性的?<span id="26"></span>

使用數(shù)字證書和CA來驗證身份,首先服務(wù)端先向CA機構(gòu)去申請證書靡狞,CA審核之后會給一個數(shù)字證書耻警,里面包裹公鑰甸怕、簽名、有效期温兼,用戶信息等各種信息式曲,在客戶端發(fā)送請求時吝羞,服務(wù)端會把數(shù)字證書發(fā)給客戶端,然后客戶端會通過信任鏈來驗證數(shù)字證書是否是有效的敦腔,來驗證服務(wù)端的身份符衔。

使用摘要算法來驗證完整性糟袁,也就是說在發(fā)送消息時判族,會對消息的內(nèi)容通過摘要算法生成一段摘要,在收到收到消息時也使用同樣的算法生成摘要项戴,來判斷摘要是否一致形帮。

27. 如何用Charles抓HTTPS的包?其中原理和流程是什么?<span id="27"></span>

流程:

  1. 首先在手機上安裝Charles證書
  2. 在代理設(shè)置中開啟Enable SSL Proxying
  3. 之后添加需要抓取服務(wù)端的地址

原理:

Charles作為中間人辩撑,對客戶端偽裝成服務(wù)端界斜,對服務(wù)端偽裝成客戶端。簡單來說:

  • 截獲客戶端的HTTPS請求合冀,偽裝成中間人客戶端去向服務(wù)端發(fā)送HTTPS請求
  • 接受服務(wù)端返回各薇,用自己的證書偽裝成中間人服務(wù)端向客戶端發(fā)送數(shù)據(jù)內(nèi)容。

具體流程如下圖,圖片來自扯一扯HTTPS單向認(rèn)證君躺、雙向認(rèn)證峭判、抓包原理、反抓包策略

image

28. 什么是中間人攻擊晰洒?如何避免朝抖?<span id="28"></span>

中間人攻擊就是截獲到客戶端的請求以及服務(wù)器的響應(yīng),比如Charles抓取HTTPS的包就屬于中間人攻擊谍珊。

避免的方式:客戶端可以預(yù)埋證書在本地治宣,然后進行證書的比較是否是匹配的

29. 了解編譯的過程么?分為哪幾個步驟砌滞?<span id="29"></span>

  1. 預(yù)編譯:主要處理以“#”開始的預(yù)編譯指令侮邀。
  2. 編譯:
    1. 詞法分析:將字符序列分割成一系列的記號。
    2. 語法分析:根據(jù)產(chǎn)生的記號進行語法分析生成語法樹。
    3. 語義分析:分析語法樹的語義亡笑,進行類型的匹配、轉(zhuǎn)換晰甚、標(biāo)識等地回。
    4. 中間代碼生成:源碼級優(yōu)化器將語法樹轉(zhuǎn)換成中間代碼穿香,然后進行源碼級優(yōu)化,比如把 1+2 優(yōu)化為 3。中間代碼使得編譯器被分為前端和后端雁歌,不同的平臺可以利用不同的編譯器后端將中間代碼轉(zhuǎn)換為機器代碼,實現(xiàn)跨平臺乏盐。
    5. 目標(biāo)代碼生成:此后的過程屬于編譯器后端净神,代碼生成器將中間代碼轉(zhuǎn)換成目標(biāo)代碼(匯編代碼)爱榕,其后目標(biāo)代碼優(yōu)化器對目標(biāo)代碼進行優(yōu)化,比如調(diào)整尋址方式、使用位移代替乘法坑夯、刪除多余指令仗谆、調(diào)整指令順序等。
  3. 匯編:匯編器將匯編代碼轉(zhuǎn)變成機器指令狸吞。
  4. 靜態(tài)鏈接:鏈接器將各個已經(jīng)編譯成機器指令的目標(biāo)文件鏈接起來,經(jīng)過重定位過后輸出一個可執(zhí)行文件威始。
  5. 裝載:裝載可執(zhí)行文件、裝載其依賴的共享對象葫掉。
  6. 動態(tài)鏈接:動態(tài)鏈接器將可執(zhí)行文件和共享對象中需要重定位的位置進行修正。
  7. 最后,進程的控制權(quán)轉(zhuǎn)交給程序入口扛门,程序終于運行起來了。

30. 靜態(tài)鏈接了解么葬凳?靜態(tài)庫和動態(tài)庫的區(qū)別?<span id="30"></span>

靜態(tài)鏈接是指將多個目標(biāo)文件合并為一個可執(zhí)行文件占业,直觀感覺就是將所有目標(biāo)文件的段合并谦疾。需要注意的是可執(zhí)行文件與目標(biāo)文件的結(jié)構(gòu)基本一致佑附,不同的是是否“可執(zhí)行”。
靜態(tài)庫:鏈接時完整地拷貝至可執(zhí)行文件中权均,被多次使用就有多份冗余拷貝。
動態(tài)庫:鏈接時不復(fù)制,程序運行時由系統(tǒng)動態(tài)加載到內(nèi)存塔橡,供程序調(diào)用,系統(tǒng)只加載一次,多個程序共用刃榨,節(jié)省內(nèi)存迅栅。

31. 內(nèi)存的幾大區(qū)域为流,各自的職能分別是什么?<span id="31"></span>

  1. 棧區(qū):有系統(tǒng)自動分配并釋放,一般存放函數(shù)的參數(shù)值,局部變量等
  2. 堆區(qū):有程序員分配和釋放缴阎,若程序員未釋放,則在程序結(jié)束時有系統(tǒng)釋放,在iOS里創(chuàng)建出來的對象會放在堆區(qū)
  3. 數(shù)據(jù)段:字符串常量肛跌,全局變量西饵,靜態(tài)變量
  4. 代碼段:編譯之后的代碼

32. static和const有什么區(qū)別?<span id="32"></span>

const是指聲明一個常量
static修飾全局變量時驯嘱,表示此全局變量只在當(dāng)前文件可見
static修飾局部變量時壕鹉,表示每次調(diào)用的初始值為上一次調(diào)用的值剃幌,調(diào)用結(jié)束后存儲空間不釋放

33. 了解內(nèi)聯(lián)函數(shù)么聋涨?<span id="33"></span>

內(nèi)聯(lián)函數(shù)是為了減少函數(shù)調(diào)用的開銷,編譯器在編譯階段把函數(shù)體內(nèi)的代碼復(fù)制到函數(shù)調(diào)用處

34. 什么時候會出現(xiàn)死鎖负乡?如何避免牍白?<span id="34"></span>

死鎖是指兩個或兩個以上的線程在執(zhí)行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象抖棘,若無外力作用,它們都將無法推進下去。
發(fā)生死鎖的四個必要條件:

  1. 互斥條件:一個資源每次只能被一個線程使用豹储。
  2. 請求與保持條件:一個線程因請求資源而阻塞時鞠鲜,對已獲得的資源保持不放碧信。
  3. 不剝奪條件:線程已獲得的資源,在未使用完之前荧飞,不能強行剝奪睛藻。
  4. 循環(huán)等待條件:若干線程之間形成一種頭尾相接的循環(huán)等待資源關(guān)系。

只要上面四個條件有一個條件不被滿足就能避免死鎖

35. 說一說你對線程安全的理解汰寓?<span id="35"></span>

在并發(fā)執(zhí)行的環(huán)境中艇劫,對于共享數(shù)據(jù)通過同步機制保證各個線程都可以正確的執(zhí)行,不會出現(xiàn)數(shù)據(jù)污染的情況吹榴,或者對于某個資源,在被多個線程訪問時娃胆,不管運行時執(zhí)行這些線程有什么樣的順序或者交錯丧蘸,不會出現(xiàn)錯誤的行為眯娱,就認(rèn)為這個資源是線程安全的,一般來說单寂,對于某個資源如果只有讀操作精盅,則這個資源無需同步就是線程安全的帽哑,若有多個線程進行讀寫操作,則需要線程同步來保證線程安全叹俏。

36. 列舉你知道的線程同步策略妻枕?<span id="36"></span>

  1. OSSpinLock 自旋鎖,已不再安全,除了這個鎖之外屡谐,下面寫的鎖述么,在等待時,都會進入線程休眠狀態(tài)愕掏,而非忙等
  2. os_unfair_lock atomic就是使用此鎖來保證原子性的
  3. pthread_mutex_t 互斥鎖度秘,并且支持遞歸實現(xiàn)和條件實現(xiàn)
  4. NSLock,NSRecursiveLock,基本的互斥鎖,NSRecursiveLock支持遞歸調(diào)用饵撑,都是對pthread_mutex_t的封裝
  5. NSCondition,NSConditionLock剑梳,條件鎖,也都是對pthread_mutex_t的封裝
  6. dispatch_semaphore_t 信號量
  7. @synchronized 也是pthread_mutex_t的封裝

37. 有哪幾種鎖滑潘?各自的原理垢乙?它們之間的區(qū)別是什么?最好可以結(jié)合使用場景來說<span id="37"></span>

  1. 自旋鎖:自旋鎖在無法進行加鎖時语卤,會不斷的進行嘗試追逮,一般用于臨界區(qū)的執(zhí)行時間較短的場景,不過iOS的自旋鎖OSSpinLock不再安全粹舵,主要原因發(fā)生在低優(yōu)先級線程拿到鎖時羊壹,高優(yōu)先級線程進入忙等(busy-wait)狀態(tài),消耗大量 CPU 時間齐婴,從而導(dǎo)致低優(yōu)先級線程拿不到 CPU 時間油猫,也就無法完成任務(wù)并釋放鎖。這種問題被稱為優(yōu)先級反轉(zhuǎn)柠偶。
  2. 互斥鎖:對于某一資源同時只允許有一個訪問情妖,無論讀寫,平常使用的NSLock就屬于互斥鎖
  3. 讀寫鎖:對于某一資源同時只允許有一個寫訪問或者多個讀訪問诱担,iOS中pthread_rwlock就是讀寫鎖
  4. 條件鎖:在滿足某個條件的時候進行加鎖或者解鎖毡证,iOS中可使用NSConditionLock來實現(xiàn)
  5. 遞歸鎖:可以被一個線程多次獲得,而不會引起死鎖蔫仙。它記錄了成功獲得鎖的次數(shù)料睛,每一次成功的獲得鎖,必須有一個配套的釋放鎖和其對應(yīng)摇邦,這樣才不會引起死鎖恤煞。只有當(dāng)所有的鎖被釋放之后,其他線程才可以獲得鎖施籍,iOS可使用NSRecursiveLock來實現(xiàn)

38. 鏈表和數(shù)組的區(qū)別是什么居扒?插入和查詢的時間復(fù)雜度分別是多少?<span id="38"></span>

鏈表和數(shù)組都是一個有序的集合丑慎,數(shù)組需要連續(xù)的內(nèi)存空間喜喂,而鏈表不需要瓤摧,鏈表的插入刪除的時間復(fù)雜度是O(1),數(shù)組是O(n)玉吁,根據(jù)下標(biāo)查詢的時間復(fù)雜度數(shù)組是O(1)照弥,鏈表是O(n),根據(jù)值查詢的時間復(fù)雜度,鏈表和數(shù)組都是O(n)

39. 哈希表是如何實現(xiàn)的进副?如何解決地址沖突产喉?<span id="39"></span>

哈希表是也是通過數(shù)組來實現(xiàn)的,首先對key值進行哈细一幔化得到一個整數(shù)曾沈,然后對整數(shù)進行計算,得到一個數(shù)組中的下標(biāo)鸥昏,然后進行存取塞俱,解決地址沖突常用方法有開放定址法和鏈表法。runtime源碼的存放weak指針哈希表使用的就是開放定址法吏垮,Java里的HashMap使用的是鏈表法障涯。

40. 排序題:冒泡排序,選擇排序膳汪,插入排序唯蝶,快速排序(二路,三路)能寫出那些遗嗽?<span id="40"></span>

這里簡單的說下幾種快速排序的不同之處粘我,隨機快排,是為了解決在近似有序的情況下痹换,時間復(fù)雜度會退化為O(n2),雙路快排是為了解決快速排序在大量數(shù)據(jù)重復(fù)的情況下征字,時間復(fù)雜度會退化為O(n2),三路快排是在大量數(shù)據(jù)重復(fù)的情況下娇豫,對雙路快排的一種優(yōu)化匙姜。

  1. 冒泡排序
extension Array where Element : Comparable{
    public mutating func bubbleSort() {
        let count = self.count
        for i in 0..<count {
            for j in 0..<(count - 1 - i) {
                if self[j] > self[j + 1] {
                    (self[j], self[j + 1]) = (self[j + 1], self[j])
                }
            }
        }
    }
}
  1. 選擇排序
extension Array where Element : Comparable{
    public mutating func selectionSort() {
        let count = self.count
        for i in 0..<count {
            var minIndex = i
            for j in (i+1)..<count {
                if self[j] < self[minIndex] {
                    minIndex = j
                }
            }
            (self[i], self[minIndex]) = (self[minIndex], self[i])
        }
    }
}
  1. 插入排序
extension Array where Element : Comparable{
    public mutating func insertionSort() {
        let count = self.count
        guard count > 1 else { return }
        for i in 1..<count {
            var preIndex = i - 1
            let currentValue = self[i]
            while preIndex >= 0 && currentValue < self[preIndex] {
                self[preIndex + 1] = self[preIndex]
                preIndex -= 1
            }
            self[preIndex + 1] = currentValue
        }
    }
}
  1. 快速排序
extension Array where Element : Comparable{
    public mutating func quickSort() {
        func quickSort(left:Int, right:Int) {
            guard left < right else { return }
            var i = left + 1,j = left
            let key = self[left]
            while i <= right {
                if self[i] < key {
                    j += 1
                    (self[i], self[j]) = (self[j], self[i])
                }
                i += 1
            }
            (self[left], self[j]) = (self[j], self[left])
            quickSort(left: j + 1, right: right)
            quickSort(left: left, right: j - 1)
        }
        quickSort(left: 0, right: self.count - 1)
    }
}
  1. 隨機快排
extension Array where Element : Comparable{
    public mutating func quickSort1() {
        func quickSort(left:Int, right:Int) {
            guard left < right else { return }
            let randomIndex = Int.random(in: left...right)
            (self[left], self[randomIndex]) = (self[randomIndex], self[left])
            var i = left + 1,j = left
            let key = self[left]
            while i <= right {
                if self[i] < key {
                    j += 1
                    (self[i], self[j]) = (self[j], self[i])
                }
                i += 1
            }
            (self[left], self[j]) = (self[j], self[left])
            quickSort(left: j + 1, right: right)
            quickSort(left: left, right: j - 1)
        }
        quickSort(left: 0, right: self.count - 1)
    }
}
  1. 雙路快排
extension Array where Element : Comparable{
    public mutating func quickSort2() {
        func quickSort(left:Int, right:Int) {
            guard left < right else { return }
            let randomIndex = Int.random(in: left...right)
            (self[left], self[randomIndex]) = (self[randomIndex], self[left])
            var l = left + 1, r = right
            let key = self[left]
            while true {
                while l <= r && self[l] < key {
                    l += 1
                }
                while l < r && key < self[r]{
                    r -= 1
                }
                if l > r { break }
                (self[l], self[r]) = (self[r], self[l])
                l += 1
                r -= 1
            }
            (self[r], self[left]) = (self[left], self[r])
            quickSort(left: r + 1, right: right)
            quickSort(left: left, right: r - 1)
        }
        quickSort(left: 0, right: self.count - 1)
    }
}

  1. 三路快排
// 三路快排
extension Array where Element : Comparable{
    public mutating func quickSort3() {
        func quickSort(left:Int, right:Int) {
            guard left < right else { return }
            let randomIndex = Int.random(in: left...right)
            (self[left], self[randomIndex]) = (self[randomIndex], self[left])
            var lt = left, gt = right
            var i = left + 1
            let key = self[left]
            while i <= gt {
                if self[i] == key {
                    i += 1
                }else if self[i] < key{
                    (self[i], self[lt + 1]) = (self[lt + 1], self[i])
                    lt += 1
                    i += 1
                }else {
                    (self[i], self[gt]) = (self[gt], self[i])
                    gt -= 1
                }
                
            }
            (self[left], self[lt]) = (self[lt], self[left])
            quickSort(left: gt + 1, right: right)
            quickSort(left: left, right: lt - 1)
        }
        quickSort(left: 0, right: self.count - 1)
    }
}

41. 鏈表題:如何檢測鏈表中是否有環(huán)?如何刪除鏈表中等于某個值的所有節(jié)點冯痢?<span id="41"></span>

  1. 如何檢測鏈表中是否有環(huán)氮昧?
public class ListNode {
    public var val: Int
    public var next: ListNode?
    public init(_ val: Int) {
        self.val = val
        self.next = nil
    }
}

extension ListNode {
    var hasCycle: Bool {
        var slow:ListNode? = self
        var fast = self.next
        while fast != nil {
            if slow! === fast! {
                return true
            }
            slow = slow?.next
            fast = fast?.next?.next
        }
        return false
    }
}
  1. 如何刪除鏈表中等于某個值的所有節(jié)點?
func remove(with value:Int, from listNode:ListNode?) -> ListNode? {
    let tmpNode = ListNode(0)
    tmpNode.next = listNode
    var currentNode = tmpNode.next
    var persiousNode:ListNode? = tmpNode
    while currentNode != nil {
        if let nodeValue = currentNode?.val, nodeValue == value {
            persiousNode?.next = currentNode?.next
        }else {
            persiousNode = currentNode
        }
        currentNode = currentNode?.next
    }
    return tmpNode.next
}

42. 數(shù)組題:如何在有序數(shù)組中找出和等于給定值的兩個元素浦楣?如何合并兩個有序的數(shù)組之后保持有序袖肥?<span id="42"></span>

  1. 如何在有序數(shù)組中找出和等于給定值的兩個元素?LeetCode第167題
func twoSum(_ numbers: [Int], _ target: Int) -> [Int] {
    var i = 0, j = numbers.count - 1
    while i < j {
        let sum = numbers[i] + numbers[j]
        if sum == target {
            return [i + 1, j + 1]
        }else if sum > target {
            j -= 1
        }else {
            i += 1
        }
    }
    return []
}
  1. 如何合并兩個有序的數(shù)組之后保持有椒振?LeetCode第88題
func merge(_ nums1: inout [Int], _ m: Int, _ nums2: [Int], _ n: Int) {
    for i in stride(from: n + m - 1, to: n - 1, by: -1) {
        nums1[i] = nums1[i - n]
    }
    var i = 0, j = 0
    while i < m && j < n {
        if nums1[n + i] > nums2[j] {
            nums1[i + j] = nums2[j]
            j += 1
        }else {
            nums1[i + j] = nums1[n + i]
            i += 1
        }
    }
    while i < m {
        nums1[i + j] = nums1[n + i]
        i += 1
    }
    while j < n {
        nums1[i + j] = nums2[j]
        j += 1
    }
}

43. 二叉樹題:如何反轉(zhuǎn)二叉樹昭伸?如何驗證兩個二叉樹是完全相等的梧乘?<span id="43"></span>

  1. 如何翻轉(zhuǎn)二叉樹澎迎?LeetCode第226題
func invertTree(_ root: TreeNode?) -> TreeNode? {
    guard let root = root else { return nil }
    (root.left, root.right) = (root.right, root.left)
    invertTree(root.left)
    invertTree(root.right)
    return root
}
  1. 如何驗證兩個二叉樹是完全相等的庐杨?
func isSameTree(_ p: TreeNode?, _ q: TreeNode?) -> Bool {
    guard let pNode = p ,let qNode = q else { return q == nil && p == nil }
    return pNode.val == qNode.val && isSameTree(pNode.left, qNode.left) && isSameTree(pNode.right, qNode.right)
}

參考

iOS 模塊詳解—「Runloop 面試、工作」看我就 ?? 了 _.

深入理解RunLoop

離屏渲染優(yōu)化詳解:實例示范+性能測試

史上最強無痕埋點

扯一扯HTTPS單向認(rèn)證夹供、雙向認(rèn)證灵份、抓包原理、反抓包策略

深入理解 iOS 開發(fā)中的鎖

如何理解互斥鎖哮洽、條件鎖填渠、讀寫鎖以及自旋鎖?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鸟辅,一起剝皮案震驚了整個濱河市氛什,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌匪凉,老刑警劉巖枪眉,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異再层,居然都是意外死亡贸铜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門聂受,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蒿秦,“玉大人,你說我怎么就攤上這事蛋济」鞅睿” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵碗旅,是天一觀的道長鹊杖。 經(jīng)常有香客問我,道長扛芽,這世上最難降的妖魔是什么骂蓖? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮川尖,結(jié)果婚禮上登下,老公的妹妹穿的比我還像新娘。我一直安慰自己叮喳,他們只是感情好被芳,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著馍悟,像睡著了一般畔濒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锣咒,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天侵状,我揣著相機與錄音赞弥,去河邊找鬼。 笑死趣兄,一個胖子當(dāng)著我的面吹牛绽左,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播艇潭,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼拼窥,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蹋凝?” 一聲冷哼從身側(cè)響起鲁纠,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鳍寂,沒想到半個月后房交,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡伐割,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年候味,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片隔心。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡白群,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出硬霍,到底是詐尸還是另有隱情帜慢,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布唯卖,位于F島的核電站粱玲,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏拜轨。R本人自食惡果不足惜抽减,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望橄碾。 院中可真熱鬧卵沉,春花似錦、人聲如沸法牲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拒垃。三九已至停撞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悼瓮,已是汗流浹背戈毒。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工艰猬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人副硅。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓姥宝,卻偏偏與公主長得像翅萤,于是被迫代替她去往敵國和親恐疲。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 最全的iOS面試題及答案 iOS面試小貼士 ———————————————回答好下面的足夠了-----------...
    zweic閱讀 2,702評論 0 73
  • OC的理解與特性 OC作為一門面向?qū)ο蟮恼Z言套么,自然具有面向?qū)ο蟮恼Z言特性:封裝培己、繼承、多態(tài)胚泌。它既具有靜態(tài)語言的特性...
    克魯?shù)吕?/span>閱讀 450評論 0 0
  • OC的理解與特性O(shè)C作為一門面向?qū)ο蟮恼Z言省咨,自然具有面向?qū)ο蟮恼Z言特性:封裝、繼承玷室、多態(tài)零蓉。它既具有靜態(tài)語言的特性(...
    LIANMING_LI閱讀 513評論 0 0
  • 今年天氣真是,突然才發(fā)現(xiàn)前段時間在貴州的日子真的很舒服穷缤,早上起來會覺得涼嗖嗖的敌蜂,晚上睡覺必須得蓋被子,穿短袖還得披...
    買金礦閱讀 261評論 6 4
  • 在一段關(guān)系中津肛,親情章喉、友情或愛情,你是付出多的那個身坐,還是少的那個秸脱? 抑或,自己的付出部蛇,值摊唇,還是不值? 1 先撇開是否...
    馬貝格閱讀 465評論 0 0