面試題

面試題

  1. 一個NSObject對象占用多少內存?

    • 實際上分配了16個字節(jié)的存儲空間給NSObject對象
    • 真正有使用的空間是:一個指針變量所占用的大小(64位:8個字節(jié)惑艇,32位:4個字節(jié))
    • 結構體:繼承遵循內存對齊原則:結構體的最終大小必須是最大成員大小的倍數。如果父類內存對齊后有多余的字節(jié),子類繼承后聲明的變量可以放到父類多余的字節(jié)當中管毙。并且OC底層定義小于16個字節(jié)的,都給分配16個字節(jié)桌硫;InstanceSize:最小對齊單位為isa指針的大小8夭咬。malloc_size:最小對齊單位為OC定義的最小字節(jié)大小16。
  2. 對象的isa指針指向哪里铆隘?

    • instance的isa指向class卓舵;
      • 當調用對象方法時,通過instance的isa找到class膀钠,最后找到對象方法的實現進行調用掏湾。
    • class的isa指向meta-class;
      • 當調用類方法時肿嘲,通過class的isa找到meta-class融击,最后找到類方法的實現進行調用。
    • meta-class的isa指向基類的meta-class睦刃;
    • 基類的meta-class的isa指向自己砚嘴;
  3. 對象的superclass指針指向哪里?

    • class的superclass指向父類的class涩拙;
      • 如果沒有父類际长,superclass指針為nil。
    • meta-class的superclass指向父類的meta-class兴泥;
    • 基類的meta-class的superclass指向基類的class工育;
  4. OC的類信息存放在哪里?

    • 搓彻;
  5. Objective-C中的對象如绸,簡稱OC對象,主要可以分為3種:

    • instance對象(實例對象):通過類alloc出來的對象旭贬,每次調用alloc都會產生新的instance對象怔接。
    • class對象(類對象):
      • objectClass1 ~ objectClass5 都是NSObject的class對象(類對象)。
      • 它們是同一個對象稀轨,每個類在內存中有且只有一個class對象扼脐。
    • meta-class對象(元類對象):
      • 每個類在內存中有且只有一個meta-class對象。
      • meta-class對象和class對象的內存結構是一樣的奋刽,但用途不一樣瓦侮。
  6. instance對象在內存中存儲的信息包括:

    • isa指針艰赞;
    • 其他成員變量;
  7. class對象在內存中存儲的信息包括:

    • isa指針肚吏;
    • superclass指針方妖;
    • 類的屬性信息(@property)
    • 類的對象方法信息(instance method)
    • 類的協(xié)議信息(protocol)
    • 類的成員變量信息(ivar)
    • ......
  8. meta-class對象在內存中存儲的信息包括:

    • isa指針
    • superclas指針
    • 類的類方法信息(class method)
    • ......(其他類似class的信息,是空的)
  9. iOS用什么方式實現對一個對象的KVO罚攀?(KVO的本質是什么党觅?)

    • 利用Runtime的API動態(tài)生成一個子類,并且讓instance對象的isa指向這個全新的子類
    • 當修改instance對象的屬性時坞生,會調用Foundation的_NSSetXXXValueAndNotify函數:
      • willChangeValueForKey
      • 父類原來的setter實現
      • didChangeValueForKey仔役,這個方法內部又會調用監(jiān)聽器(observer)的監(jiān)聽方法
        • 內部又會調用監(jiān)聽器(observer)的監(jiān)聽方法:observeValueForKeyPath:ofObject:change:context:
  10. _NSSetXXXValueAndNotify的內部實現:

    • 調用willChangeValueForKey
    • 調用原來的setter實現
    • 調用didChangeValueForKey
      • didChangeValueForKey內部會調用observer的observeValueForKeyPath:ofObject:change:context方法
  11. 如何手動觸發(fā)KVO掷伙?

    • 手動調用willChangeValueForKey和didChangeValueForKey是己;
  12. 通過KVC修改屬性會觸發(fā)KVO嗎?

    • 會觸發(fā)KVO(相當于setValue:forKey:內部手動調用了KVO的_NSSetXXXValueAndNotify方法)
  13. KVC:setValue:forkey:的原理:

    • 按照setKey任柜、_setKey順序查找方法:
      • 如果找到了傳遞參數卒废,調用方法。
      • 如果找不到宙地,查看accessInstanceVariableDirectory方法的返回值:
        • 返回NO:調用setValue:forUndefinedKey:并拋出異常NSUnknownKeyException
        • 返回YES:按照_key摔认、_isKey、key宅粥、isKey順序查找成員變量参袱,查找到直接賦值,查找不到拋出同上NO的異常秽梅。
  14. KVC:valueForKey:的原理:

    • 按照getKey抹蚀、key、isKey企垦、_key順序查找方法:
      • 如果找到了环壤,調用方法。
      • 如果找不到钞诡,查看accessInstanceVariableDirectory方法的返回值:
        • 返回NO:調用valueForUndefinedKey:并拋出異常NSUnknownKeyException
        • 返回YES:按照_key郑现、_isKey、key荧降、isKey順序查找成員變量接箫,查找到直接取值,查找不到拋出同上NO的異常朵诫。
  15. KVC的賦值和取值過程是怎樣的辛友?原理是什么?

    • 賦值過程即上面的setValue:forKey:的原理拗窃;
    • 取值過程即上面的valueForKey:的原理瞎领;
  16. 什么是Runloop泌辫?

    • 運行循環(huán)
    • 在程序運行過程中循環(huán)做一些事情
  17. runloop的基本作用:

    • 保持程序的持續(xù)運行
    • 處理APP中的各種事件(比如觸摸事件、定時器事件等)
    • 節(jié)省CPU資源九默,提高程序性能:該做事時做事震放,該休息時休息
  1. runloop內部實現邏輯?

    • sources0:
      • 觸摸事件處理驼修;
      • performSelector:onThread:
    • sources1:
      • 基于port的線程間通信殿遂;
      • 系統(tǒng)事件捕捉(比如點擊事件是sources1捕捉,然后分發(fā)給sources0去處理)乙各;
    • timers:
      • NSTimer墨礁;
      • performSelector:withObject:afterDelay;
    • observers:
      • 用于監(jiān)聽RunLoop的狀態(tài)耳峦;
      • UI刷新(BeforeWaiting)恩静;
      • Autorelease pool;
  2. runloop和線程的關系蹲坷?

    • 每條線程都有唯一的一個與之對應的RunLoop對象
    • RunLoop保存在一個全局的Dictionary里驶乾,線程作為key,RunLoop作為Value
    • 線程剛創(chuàng)建時并沒有RunLoop對象循签,RunLoop會在第一次獲取它時創(chuàng)建
    • RunLoop會在線程結束時銷毀
  3. RunLoop休眠的實現原理:

    • 休眠:從用戶態(tài)切換到內核態(tài):
    • 內核態(tài):等待消息级乐;
      • 沒有消息就讓線程休眠;
      • 有消息就喚醒線程县匠;
    • 喚醒:從內核態(tài)切換到用戶態(tài)风科,來處理消息;
  4. RunLoop的幾種狀態(tài)乞旦?

    • 贼穆;
  1. Timer與RunLoop的關系?

    • Timer是運行在RunLoop里面的杆查;
  2. RunLoop是怎么響應用戶操作的扮惦,具體流程是什么樣的?

    • sources1捕捉事件亲桦;
    • 交給sources0去處理崖蜜;
  3. Core Foundation中關于RunLoop的5個類:

    • CFRunLoopRef
    • CFRunLoopModeRef
    • CFRunLoopSourceRef
    • CFRunLoopTimerRef
    • CFRunLoopObserverRef
  4. CFRunLoopModeRef:

    • CFRunLoopModeRef代表RunLoop的運行模式。
    • 一個RunLoop包含若干個Mode客峭,每個Mode又包含若干個Source0/Source1/Timer/Observer豫领。
    • RunLoop啟動時只能選擇其中一個Mode,作為currentMode舔琅。
    • 如果需要切換Mode等恐,只能退出當前Loop,再重新選擇一個Mode進入。
      • 不同組的Source0/Source1/Timer/Observer能分割開來课蔬,互不影響囱稽。
    • 如果Mode里沒有任何Source0/Source1/Timer/Observer,RunLoop會立馬退出二跋。
  5. CFRunLoopModeRef:目前一直的Mode有5種:

    • kCFRunLoopDefaultMode:APP的默認Mode战惊,通常主線程是在這個Mode下運行。
    • UITrackingRunLoopMode:界面跟蹤Mode扎即,用于ScrollView追蹤觸摸滑動吞获,保證界面滑動時不受其他Mode影響。
    • kCFRunLoopCommonModes:這是一個占位用的Mode谚鄙,不是一個真正的Mode各拷。
    • UIInitializationRunLoopMode:在剛啟動APP時進入的第一個Mode,啟動完成后就不再使用闷营。
    • GSEventReceiveRunLoopMode:接受系統(tǒng)事件的內部Mode烤黍,通常用不到。
  6. Category的實現原理是什么粮坞?

    • Category編譯之后的底層結構是struct category_t:里面存儲著分類的對象方法蚊荣、類方法初狰、屬性莫杈、協(xié)議信息。
    • 在程序運行的時候奢入,Runtime會將Category的數據筝闹,喝杯冰島類信息中(類對象、元類對象中)
  7. Category和Class Extension的區(qū)別是什么腥光?

    • Class Extension在編譯的時候关顷,它的數據就已經包含在類信息中。
    • Category是在運行時武福,才將數據合并到類信息中议双。
  8. Category中有l(wèi)oad方法嗎?load方法是什么時候調用的捉片?load方法能繼承嗎平痰?

    • 有l(wèi)oad方法;
    • 在Runtime加載類伍纫、分類的時候調用宗雇;
    • +load方法可以繼承,但是一般不會主動去調用load方法莹规,都是讓系統(tǒng)自動調用赔蒲。
  9. 分類的對象方法、類方法也是分別存放在類對象、元類對象的方法列表舞虱。類里面的方法是在編譯時就放進去欢际,分類是通過runtime動態(tài)將分類的方法合并到類對象、元類對象中矾兜。

  10. 分類里面添加屬性:

    • 只會生成set幼苛、get方法的聲明;
    • 不會生成set焕刮、get方法的具體實現舶沿;
    • 不會生成屬性的成員變量;
  11. Category的加載處理過程:

    • 通過Runtime加載某個類的所有Category數據配并。
    • 把所有Category的方法括荡、屬性、協(xié)議數據溉旋,合并到一個大數組中畸冲。
      • 后面參與編譯的Category數據,會被放在數組的前面观腊。
    • 將合并后的數據(方法邑闲、屬性、協(xié)議)梧油,插入到類原來數據的前面苫耸。
  12. +load方法:

    • +load方法會在Runtime加載類、分類時調用儡陨。(是通過指針直接找到方法調用的褪子,不是通過消息機制調用)
    • 每個類、分類的+load骗村,在程序運行過程中只調用一次嫌褪。
    • 調用順序:
      • 先調用類的+load;
        • 按照編譯先后順序調用(先編譯胚股,先調用)
        • 調用子類的+load之前會先調用父類的+load笼痛;
      • 再調用分類的+load;
        • 按照編譯先后順序調用(先編譯琅拌,先調用)(不會先調用父類的分類)缨伊。
  13. load、initialize方法的區(qū)別是什么财忽?它們在category中的調用的順序倘核?以及出現繼承時他們之間的調用過程?

    • 調用方式:
      • +load是直接找到對應的方法地址直接調用即彪;
      • +initialize是通過objc_msgSend調用的紧唱;
    • 調用時刻:
      • +load是Runtime加載類活尊、分類的時候調用(只會調用一次)
      • +initialize是類第一次接收到消息的時候調用,每一個類只會initialize一次(父類的initialize方法可能會被調用多次)
  14. +initialize方法:

    • +initialize方法會在類第一次接收消息時調用漏益;(如果從來沒接收過消息蛹锰,就不會調用)
    • 調用順序:
      • 先初始化父類的+initialize,
      • 再初始化子類的+initialize绰疤;(可能最終調用的是父類的initialize方法铜犬,但不代表又初始化了父類,只是調用了父類的方法轻庆,初始化的是子類癣猾,因為每個類只會被初始化一次)
    • 只會初始化一次;(如果子類沒有實現+initialize余爆,會調用父類的+initialize纷宇;(所以父類的+initialize可能會被調用多次))
    • +initialize是通過objc_msgSend進行調用的;所以具備以下特點:
      • 如果分類實現了+initialize蛾方,就會覆蓋類本身的+initialize調用像捶。
      • 如果子類沒有實現+initialize,會調用父類的+initialize桩砰;(所以父類的+initialize可能會被調用多次)
  15. Category能否添加成員變量拓春?如果可以,如何給Category添加成員變量亚隅?

    • 不可以直接給Category添加成員變量硼莽;
    • 但是可以通過添加關聯對象,間接實現Category有成員變量的效果枢步;
  16. 如何實現給分類添加關聯對象沉删?

  17. block的原理是怎樣的醉途?本質是什么?

    • block本質上也是一個OC對象砖茸,它內部也有個isa指針隘擎;
    • block是封裝了函數調用以及函數調用環(huán)境的OC對象;
    • block的底層結構如圖:
      截屏2021-07-25 上午11.59.07.png
  18. block的變量捕獲(capture)

    • 為了保證block內部能夠正常訪問外部的變量凉夯,block有個變量捕獲機制:
      • 局部變量:auto:能夠捕獲到block內部货葬。訪問方式:值傳遞。
      • 局部變量:static:能夠捕獲到block內部劲够。訪問方式:指針傳遞震桶。
      • 全局變量:不能捕獲到block內部。訪問方式:直接訪問征绎;
  19. Block的類型:Block有3種類型蹲姐,可以通過調用class方法或者isa指針查看具體類型,最終都是繼承自NSBlock類型:

    • NSGlobalBlock (_NSConcreteGlobalBlock)(數據段:全局變量):只要沒有訪問auto變量的,都是Global柴墩。Global調用Copy后忙厌,什么也不必做。
    • NSMallocBlock(_NSConcreteMallocBlock)(堆段:alloc出來的內容江咳,動態(tài)分配內存逢净,需要程序員申請內存、管理內存歼指,比如free〉粒現在有ARC): NSStackBlock 調用了Copy后,就是Malloc踩身。Malloc調用Copy后着饥,引用計數加1;
    • NSStackBlock(_NSConcreteStackBlock)(棧段:局部變量惰赋,離開作用域自動銷毀):訪問了auto變量宰掉,默認就是Stack。(沒有ARC的情況下赁濒,因為ARC做了事情) NSStackBlock 調用了Copy轨奄,會從棧復制到堆,堆上的就變成了Malloc拒炎。
  20. Block的Copy:在ARC環(huán)境下挪拟,編譯器會根據情況自動將棧上的block復制到堆上,比如以下情況:

    • block作為函數返回值時击你;
    • 將block賦值給__strong指針時玉组;
    • block作為Cocoa API中方法名含有usingBlock的方法參數時;
    • block作為GCD的方法參數時丁侄;
  21. 當block內部訪問了對象類型的auto變量時:

    • 如果block是在棧上惯雳,將不會對auto變量產生強引用(不管是ARC、MRC都不會)
    • 如果block被拷貝到堆上:
      • 會調用block內部的Copy函數鸿摇;
      • Copy函數內部會調用_Block_object_assign函數
      • _Block_object_assign函數會根據auto變量的修飾符(__strong石景、__weak、__unsafe_unretained)做出相應的操作拙吉,類似于retain(形成強引用潮孽、弱引用)
    • 如果block從堆上移除:
      • 會調用block內部的dispose函數;
      • dispose函數內部會調用_Block_object_dispose函數筷黔;
      • _Block_object_dispose函數會自動釋放引用的auto變量往史,類似于release;
  22. 被__block修飾的對象類型:__block MJPersion *person = [[MSPersion alloc] init];

    • 當__block變量在棧上時佛舱,不會對指向的對象產生強引用椎例;
    • 當__block變量Copy到堆時:
      • 會調用__block變量內部的Copy函數挨决;
      • Copy函數內部會調用_Block_object_assign函數;
      • _Block_object_assign函數會根據所指向對象的修飾符(__strong粟矿、__weak凰棉、__unsafe_unretained)做出相應的操作,形成強引用(retain)或弱引用陌粹;(注意:這里僅限于ARC時會retain撒犀,MRC時不會retain)
    • 如果__block變量從堆上移除:
      • 會調用__block變量內部的dispose函數;
      • dispose函數內部會調用_Block_object_dispose函數掏秩;
      • _Block_object_dispose函數會自動釋放指向的對象(release)或舞;
  23. __block的作用是什么?有什么使用注意點蒙幻?__block修飾符:

    • __block可以用于解決block內部無法修改auto變量值的問題映凳;
    • __block不能修飾全局變量、靜態(tài)變量(static)邮破;
    • 編譯器會將__block變量包裝成一個對象诈豌;
  24. __block的內存管理:

    • 當block在棧上時,并不會對__block變量產生強引用抒和;
    • 當block被Copy到堆時:
      • 會調用block內部的Copy函數矫渔;
      • Copy函數內部會調用_Block_object_assign函數;
      • _Block_object_assign函數會對__block變量形成強引用(retain)(__block變量也會從棧上復制到堆上摧莽,是堆上的block對堆上的__block變量形成強引用)
    • 當block從堆中移除時:
      • 會調用block內部的dispose函數庙洼;
      • dispose函數內部會調用_Block_object_dispose函數;
      • _Block_object_dispose函數會自動釋放引用的__block變量(release)镊辕;
  25. 解決循環(huán)引用問題:

    • ARC環(huán)境下:
      • __weak解決:不會產生強引用油够,指向的對象銷毀時,會自動讓指針置為nil征懈;
      • __unsafe_unretained解決:不會產生強引用石咬,不安全,指向的對象銷毀時受裹,指針存儲的地址值不變碌补;
      • __block解決:必須要調用block,并且在block內部必須要把引用的對象置為nil棉饶;
    • MRC環(huán)境下:不支持__weak;
      • __unsafe_unretained解決镇匀;
      • __block解決:不用調用block方法照藻,因為MRC下,__block變量不會對對象進行retain操作汗侵;
  26. block的屬性修飾詞為什么是copy幸缕?使用block有哪些使用注意群发?

    • block一旦沒有進行Copy操作,就不會在堆上发乔;
    • 使用注意:循環(huán)引用問題熟妓;
  27. block在修飾NSMutableArray,需不需要添加__block栏尚?

    • 不需要起愈;([array addObject:xxx],這個方法是對array里面的內容進行修改译仗,不是修改array本身抬虽,所以不需要。如果是修改array本身亏推,則是需要的公荧,比如在block里面執(zhí)行:array = nil融欧、array = [NSMutableArray alloc]);
  28. isa詳解:

    • 位域:
  29. OC中的方法調用,其實都是轉換為objc_msgSend函數的調用笛辟。objc_msgSend執(zhí)行流程:

    • 消息發(fā)送;
    • 動態(tài)方法解析序苏;
    • 消息轉發(fā)手幢;
  30. [super message]的底層實現:

    • 只是從父類開始查找方法的實現;
    • 消息接收者仍然是子類對象杠览;
  31. 什么是Runtime弯菊?

    • OC是一門動態(tài)性比較強的編程語言,允許很多操作推遲到程序運行時再進行踱阿;
    • OC的動態(tài)性就是由Runtime來支撐和實現的管钳,Runtime是一套C語言的API,封裝了很多動態(tài)性相關的函數软舌;
    • 平時編寫的OC代碼才漆,底層都是轉換成了RuntimeAPI進行調用;
  32. Runtime平時項目中有用過么佛点?

    • 利用關聯對象(AssociatedObject)給分類添加屬性醇滥;
    • 遍歷類的所有成員變量(修改TextField的占位文字顏色、字典轉模型超营、自動歸檔解檔)鸳玩;
    • 交換方法實現(交換系統(tǒng)的方法);
    • 利用消息轉發(fā)機制解決方法找不到的異常演闭;
    • weak的底層實現也是依賴于Runtime不跟;
    • ...
  33. 類簇:NSString、NSArray米碰、NSDictionary窝革,真是類型是其他類型购城;

  34. RunLoop的應用范疇?

    • 定時器(Timer)虐译、PerformSelector瘪板;
    • GCD Async Main Queue;
    • 事件響應漆诽、手勢識別侮攀、界面刷新;
    • 網絡請求拴泌;
    • AutoreleasePool魏身;
  35. RunLoop在實際開發(fā)中的應用:

    • 控制線程生命周期(線程保活蚪腐,比如AFNetworking)箭昵;
    • 解決NSTimer在滑動時停止工作的問題;
    • 監(jiān)控應用卡頓回季;
    • 性能優(yōu)化家制;
  36. iOS中的常見多線程方案:

    • pthread:
      • C語言;
      • 線程生命周期:程序員管理泡一;
      • 簡介:
        • 一套通用的多線程API颤殴;
        • 適用于Unix、Linux鼻忠、Windows等系統(tǒng)涵但;
        • 跨平臺、可移植帖蔓;
        • 使用難度大矮瘟;
      • 使用頻率:幾乎不用;
    • NSThread塑娇;
      • OC語言澈侠;
      • 線程生命周期:程序員管理;
      • 簡介:
        • 使用更加面向對象埋酬;
        • 簡單易用哨啃,可直接操作線程對象;
        • 其實底層是pthread写妥;
      • 使用頻率:幾乎不用拳球;
    • GCD:
      • C語言;
      • 線程生命周期:自動管理珍特;
      • 簡介:
        • 旨在替代NSThread等線程技術醇坝;
        • 充分利用設備的多核;
        • 其實底層是pthread次坡;
      • 使用頻率:經常使用呼猪;
    • NSOperation:
      • OC語言;
      • 線程生命周期:自動管理砸琅;
      • 簡介:
        • 基于GCD(底層是GCD)宋距;
        • 比GCD多了一些更簡單實用的功能;
        • 使用更加面向對象症脂;
        • 其實底層是pthread谚赎;
      • 使用頻率:經常使用;
  37. GCD的常用函數的執(zhí)行方式:

    • 同步:dispatch_sync(dispatch_queue_t queue, dispatch_block_t block):
      • queue:隊列诱篷;
      • block:任務壶唤;
    • 異步:dispatch_async(dispatch_queue_t queue, dispatch_block_t block):
  38. GCD的隊列可以分為2大類型:

    • 并發(fā)隊列(Concurrent Dispatch Queue):
      • 可以讓多個任務并發(fā)(同時)執(zhí)行(自動開啟多線程同時執(zhí)行任務);
      • 并發(fā)功能只有在異步(dispatch_async)函數下才有效棕所;
    • 串行隊列(Serial Dispatch Queue)
      • 讓任務一個接著一個地執(zhí)行(一個任務執(zhí)行完畢后闸盔,再執(zhí)行下一個任務);
    • 主隊列:也是一種串行隊列琳省;
  39. 容易混淆的術語:

    • 同步和異步主要影響:能不能開啟新的線程迎吵;

      • 同步:在當前線程中執(zhí)行任務,不具備開啟新線程的能力针贬;
      • 異步:在新的線程中執(zhí)行任務击费,具備開啟新線程的能力;
    • 并發(fā)和串行主要影響:任務的執(zhí)行方式桦他;

      • 并發(fā):多個任務并發(fā)(同時)執(zhí)行蔫巩;
      • 串行:一個任務執(zhí)行完畢后,再執(zhí)行下一個任務快压;
  40. 各種隊列的執(zhí)行效果:

    • 同步(sync):
      • 并發(fā)隊列:
        • 沒有開啟新線程圆仔;
        • 串行執(zhí)行任務;
      • 串行隊列:
        • 沒有開啟新線程嗓节;
        • 串行執(zhí)行任務荧缘;
      • 主隊列:
        • 沒有開啟新線程;
        • 串行執(zhí)行任務拦宣;
    • 異步(async):
      • 并發(fā)隊列:
        • 會開啟新線程截粗;
        • 并發(fā)執(zhí)行任務;
      • 手動創(chuàng)建的串行隊列:
        • 會開啟新線程鸵隧;
        • 串行執(zhí)行任務绸罗;
      • 主隊列:
        • 沒有開啟新線程;
        • 串行執(zhí)行任務豆瘫;
  41. 使用sync函數往當前串行隊列中添加任務珊蟀,會卡住當前的串行隊列(產生死鎖);

  42. 多線程的安全隱患:

    • 資源共享:
      • 1塊資源可能會被多個線程共享,也就是多個線程可能會訪問同一塊資源育灸;
      • 比如多個線程訪問同一個對象腻窒、同一個變量、同一個文件磅崭;
    • 當多個線程訪問同一塊資源時儿子,很容易引發(fā)數據錯亂和數據安全問題;
  43. 多線程安全隱患的解決方案:

    • 使用線程同步技術(同步砸喻,就是協(xié)同步調柔逼,按預定的先后次序進行運行);
    • 常見的線程同步技術是:加鎖割岛;
      • OSSpinLock:自旋鎖:等待鎖的線程會處于忙等(busy-wait)狀態(tài)愉适,一直占用著CPU資源;
        • 目前已經不再安全癣漆,可能會出現優(yōu)先級反轉的問題维咸;如果等待鎖的線程優(yōu)先級較高,它會一直占用著CPU資源扑媚,優(yōu)先級低的線程就無法釋放鎖腰湾。
      • os_unfair_lock:從底層調用看,等待os_unfair_lock鎖的線程會處于休眠狀態(tài)疆股,并非忙等费坊。
        • 用于取代不安全的OSSpinLock,從iOS10開始才支持:
      • pthread_mutex:互斥鎖:等待鎖的線程會處于休眠狀態(tài)旬痹;
      • NSLock:是對pthread_mutex普通鎖的封裝附井;
      • NSRecursiveLock:是對pthread_mutex遞歸鎖的封裝;
      • NSCondition:是對pthread_mutex和cond(喚醒信號條件)的封裝两残;
      • NSConditionLock:是對NSCondition的進一步封裝永毅,可以設置具體的喚醒條件值。
      • dispatch_queue:直接使用GCD的串行隊列人弓,也是可以實現線程同步的沼死;
      • dispatch_semaphore:信號量:信號量的初始值,可以用來控制線程并發(fā)訪問的最大數量崔赌;
      • @synchronized:是對pthread_mutex遞歸鎖的封裝意蛀;
      • 遞歸鎖:允許同一個線程對一把鎖進行重復加鎖;
  44. iOS線程同步方案性能比較:性能從高到底排序:

    • os_unfair_lock健芭;
    • OSSpinLock
    • dispatch_semaphore县钥;
    • pthread_mutex;
    • dispatch_queue(DISPATCH_QUEUE_SERIAL)慈迈;
    • NSLock若贮;
    • NSCondition;
    • pthread_mutex(recursive);
    • NSRecursiveLock谴麦;
    • NSConditionLock蠢沿;
    • @synchronized;
  45. 自旋鎖细移、互斥鎖比較:

    • 什么情況使用自旋鎖比較劃算搏予?
      • 預計線程等待鎖的時間很短;
      • 加鎖的代碼(臨界區(qū))經常被調用弧轧,但競爭情況很少發(fā)生;
      • CPU資源不緊張碗殷;
      • 多核處理精绎;
    • 什么情況使用互斥鎖比較劃算?
      • 預計線程等待鎖的時間較長锌妻;
      • 單核處理器代乃;
      • 臨界區(qū)有IO操作;
      • 臨界區(qū)代碼復雜或循環(huán)量大仿粹;
      • 臨界區(qū)競爭非常激烈搁吓;
  46. atomic:

    • 用于保證屬性setter、getter的原子性操作吭历,相當于在setter和getter內部加了線程同步的鎖堕仔;
    • 它并不能保證使用屬性的過程是線程安全的;
  47. iOS中的讀寫安全方案:

    • 思考如何實現以下場景:
      • 同一時間晌区,只能有1個線程進行寫的操作:
      • 同一時間摩骨,允許有多個線程進行讀的操作:
      • 同一時間,不允許既有寫的操作朗若,又有讀的操作恼五;
    • 上面的場景就是典型的“多讀單寫”,經常用于文件等數據的讀寫操作哭懈,iOS中的實現方案有:
      • pthread_rwlock:讀寫鎖灾馒;
      • dispatch_barrier_async:異步柵欄調用:
        • 這個函數傳入的并發(fā)隊列必須是自己通過dispatch_queue_create創(chuàng)建的;
        • 如果傳入的是一個串行或是一個全局的并發(fā)隊列遣总,那這個函數便等同于dispatch_async函數的效果睬罗;
  48. CADisplayLink、NSTimer使用注意:

    • CADisplayLink彤避、NSTimer會對target產生強引用傅物,如果target又對它們產生強引用,那么就會引發(fā)循環(huán)引用琉预。
    • CADisplayLink:保證調用頻率和屏幕的刷幀頻率一致董饰,60FPS(每秒60次)。但是會受主線程的影響,所以并不能保證每秒執(zhí)行60次卒暂;
    • NSTimer:
  49. GCD定時器:

    • NSTimer依賴于RunLoop啄栓,如果RunLoop的任務過于繁重,可能會導致NSTimer不準時也祠;
    • 而GCD的定時器會更加準時:跟內核掛鉤昙楚,并且不依賴于RunLoop;
  50. iOS程序的內存布局:

    • 保留區(qū)诈嘿;
    • 代碼段堪旧;編譯之后的代碼;
    • 數據段奖亚;
      • 字符串常量:比如NSString *str = @"123";
      • 已初始化數據:已初始化的全局變量淳梦、靜態(tài)變量等;
      • 未初始化數據:未初始化的全局變量昔字、靜態(tài)變量等爆袍;
    • 堆;通過alloc作郭、malloc陨囊、calloc等動態(tài)分配的空間;(分配地址:由低到高)
    • 棧夹攒;函數調用開銷:比如函數里面的局部變量蜘醋;(分配地址:由高到低)
    • 內核區(qū);
  51. Tagged Pointer:

    • 從64bit開始芹助,iOS引入了Tagged Pointer技術堂湖,用于優(yōu)化NSNumber、NSDate状土、NSString等小對象的存儲无蜂;
    • 在沒有使用Tagged Pointer之前,NSNumber等對象需要動態(tài)分配內存蒙谓、維護引用計數等斥季,NSNumber指針存儲的是堆中NSNumber對象的地址值;
    • 使用Tagged Pointer之后累驮,NSNumber指針里面存儲的數據變成了:Tag + Data酣倾,也就是將數據直接存儲在了指針中;
    • 當指針不夠存儲數據時谤专,才會使用動態(tài)分配內存的方式來存儲數據躁锡;
    • objc_msgSend能識別Tagged Pointer,比如NSNumber的intValue方法置侍,直接從指針提取數據映之,節(jié)省了以前的調用開銷拦焚;
    • 如何判斷一個指針是否為Tagged Pointer?
      • mac平臺:指針的最低有效位是1杠输;
      • iOS平臺:指針的最高有效位是1赎败;(第64bit)
  52. OC對象的內存管理:

    • 在iOS中,使用引用計數來管理OC對象的內存蠢甲;
    • 一個新創(chuàng)建的OC對象引用計數默認是1僵刮,當引用計數減為0,OC對象就會銷毀鹦牛,釋放其占用的內存空間搞糕;
    • 調用retain會讓OC對象的引用計數+1,調用release會讓OC對象的引用計數-1能岩;
    • 內存管理的經驗總結:
      • 當調用alloc寞宫、new、copy拉鹃、mutableCopy方法返回了一個對象,在不需要這個對象時鲫忍,要調用release或autorelease來釋放它膏燕;
      • 想擁有某個對象,就讓它的引用計數+1悟民;
      • 不想擁有某個對象坝辫,就讓它的引用計數-1;
  53. 拷貝的目的:產生一個副本對象射亏,跟源對象互不影響近忙;修改了源對象,不會影響副本對象智润。修改了副本對象及舍,不會影響源對象。

  54. 深拷貝窟绷、淺拷貝:

    • 深拷貝:
      • 內容拷貝锯玛,有產生新對象;
    • 淺拷貝:
      • 指針拷貝兼蜈,沒有產生新對象攘残;
  55. copy和mutableCopy:NSString、NSMutableString为狸、NSArray歼郭、NSMutableArray、NSDictionary辐棒、NSMutableDictionary:

    • 不可變對象:copy病曾,還是不可變對象牍蜂,跟原來指向同一個內存地址,是淺拷貝知态;mutable捷兰,是可變對象,是深拷貝负敏;
    • 可變對象:copy贡茅,是不可變,是深拷貝其做;mutable顶考,是可變對象,是深拷貝妖泄;
  56. 引用計數的存儲:

    • 在64bit中驹沿,引用計數可以直接存儲在優(yōu)化過的isa指針中,也可能存儲在sideTable類中蹈胡;
    • sideTable是一個存放著對象引用計數的散列表渊季;
  57. weak指針的實現原理:

    • 將弱引用存到哈希表里面,當對象要銷毀時罚渐,就去除該對象對應的弱引用表却汉,把弱引用表里面存儲的弱引用都清除掉;
  58. autorelease對象在什么時機會被調用release荷并?

    • iOS在主線程的RunLoop中注冊了2個Observer合砂;
      • 第1個Observer監(jiān)聽了kCFRunLoopEntry事件,會調用objc_autoreleasePoolPush()源织;
      • 第2個Observer:
        • 監(jiān)聽了kCFRunLoopBeforeWaiting事件翩伪,會調用objc_autoreleasePoolPop()、objc_autoreleasePoolPush()谈息;
        • 監(jiān)聽了kCFRunLoopBeforeExit事件缘屹,會調用objc_autoreleasePoolPop();
  59. 方法里有局部對象黎茎,出了方法后會立即釋放嗎囊颅?

    • 如果局部對象是通過autorelease釋放的話,不是立即釋放傅瞻,是在對象所處的RunLoop休眠前釋放踢代;
    • 如果ARC是生成release代碼的話,是立即釋放嗅骄;
  60. 卡頓產生的原因:CPU胳挎、GPU執(zhí)行了比較耗時的操作:

    • CPU(Central Processing Unit,中央處理器):
      • 對象的創(chuàng)建和銷毀溺森;
      • 對象屬性的調整慕爬;
      • 布局計算窑眯;
      • 文本的計算和排版、圖片的格式轉換和解碼医窿;
      • 圖像的繪制(Core Graphics)磅甩;
    • GPU(Graphics Processing Unit,圖形處理器):
      • 紋理的渲染姥卢;
  61. 在iOS中是雙緩沖機制:有前幀緩存卷要、后幀緩存;

  62. 卡頓解決的主要思路:

    • 盡可能減少CPU独榴、GPU資源消耗僧叉;
  63. 按照60FPS的刷幀率,每隔16ms就會有一次VSync(垂直同步)信號棺榔;

  64. 卡頓優(yōu)化:

    • CPU:
      • 盡量用輕量級的對象瓶堕,比如用不到事件處理的地方,可以考慮使用CALayer取代UIView症歇;
      • 不要頻繁的調用UIView的相關屬性郎笆,比如frame、bounds忘晤、transform等屬性题画,盡量減少不必要的修改;
      • 盡量提前計算好布局德频,在有需要時一次性調整對應的屬性,不要多次修改屬性缩幸;
      • Autolayout會比直接設置frame消耗更多的CPU資源壹置;
      • 圖片的size最好剛好跟UIImageView的size保持一致;
      • 控制一下線程的最大并發(fā)數量表谊;
      • 盡量把耗時的操作放到子線程钞护;
        • 文本處理(尺寸計數、繪制)爆办;
        • 圖片處理(解碼难咕、繪制)
    • GPU:
      • 盡量避免短時間內大量圖片的顯示,盡可能將多張圖片合成一張進行顯示距辆;
      • GPU能處理的最大紋理尺寸是4096x4096余佃,一旦超過這個尺寸,就會占用CPU資源進行處理跨算,所以紋理盡量不要超過這個尺寸爆土;
      • 盡量減少視圖數量和層次;
      • 減少透明的視圖(alpha < 1)诸蚕,不透明的就設置opaque為YES步势;
      • 盡量避免出現離屏渲染氧猬;
  65. 離屏渲染:

    • 在OpenGL中,GPU有2種渲染方式:
      • On-Screen Rendering:當前屏幕渲染坏瘩,在當前用于顯示的屏幕緩沖區(qū)進行渲染操作盅抚;
      • Off-Screen Rendering:離屏渲染,在當前屏幕緩沖區(qū)以外新開辟一個緩沖區(qū)進行渲染操作倔矾;
    • 離屏渲染消耗性能的原因:
      • 需要創(chuàng)建新的緩沖區(qū)妄均;
      • 離屏渲染的整個過程,需要多次切換上下文環(huán)境破讨,先是從當前屏幕(On-Screen)切換到離屏(Off-Screen)丛晦;等到離屏渲染結束以后,將離屏緩沖區(qū)的渲染結果顯示到屏幕上提陶,又需要將上下文環(huán)境從離屏切換到當前屏幕烫沙;
    • 哪些操作會觸發(fā)離屏渲染?
      • 光柵化:layer.shouldRasterize = YES隙笆;
      • 遮罩:layer.mask锌蓄;
      • 圓角:同時設置layer.masksToBounds = YES、layer.cornerRadius > 0
        • 優(yōu)化:可以考慮通過CoreGraphics繪制裁剪圓角撑柔,或者叫美工提供圓角圖片;
      • 陰影:layer.shadowXXX瘸爽;
        • 但是如果陰影設置了路線:layer.shadowPath就不會產生離屏渲染;
  66. 卡頓檢測:

    • 平時所說的卡頓铅忿,主要是因為在主線程執(zhí)行了比較耗時的操作剪决;
    • 可以添加Observer到主線程RunLoop中,通過監(jiān)聽RunLoop狀態(tài)切換的耗時檀训,以達到監(jiān)控卡頓的目的柑潦;
    • 有封裝好的可以參考:LXDAppFluecyMonitor

知識點

  1. 花指令:破解峻凫、反匯編渗鬼;
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市荧琼,隨后出現的幾起案子譬胎,更是在濱河造成了極大的恐慌,老刑警劉巖命锄,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件堰乔,死亡現場離奇詭異,居然都是意外死亡累舷,警方通過查閱死者的電腦和手機浩考,發(fā)現死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來被盈,“玉大人析孽,你說我怎么就攤上這事搭伤。” “怎么了袜瞬?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵怜俐,是天一觀的道長。 經常有香客問我邓尤,道長拍鲤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任汞扎,我火速辦了婚禮季稳,結果婚禮上,老公的妹妹穿的比我還像新娘澈魄。我一直安慰自己景鼠,他們只是感情好,可當我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布痹扇。 她就那樣靜靜地躺著铛漓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鲫构。 梳的紋絲不亂的頭發(fā)上浓恶,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機與錄音结笨,去河邊找鬼包晰。 笑死,一個胖子當著我的面吹牛炕吸,可吹牛的內容都是我干的杜窄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼算途,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蚀腿?” 一聲冷哼從身側響起嘴瓤,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎莉钙,沒想到半個月后廓脆,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡磁玉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年停忿,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚊伞。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡席赂,死狀恐怖吮铭,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情颅停,我是刑警寧澤谓晌,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站癞揉,受9級特大地震影響纸肉,放射性物質發(fā)生泄漏。R本人自食惡果不足惜喊熟,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一柏肪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧芥牌,春花似錦烦味、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至扇商,卻和暖如春凤瘦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背案铺。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工蔬芥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像陶冷,于是被迫代替她去往敵國和親寻定。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內容

  • 25. _objc_msgForward 函數是做什么的柱衔,直接調用它將會發(fā)生什么?[https://github....
    攻城獅GG閱讀 282評論 0 0
  • 1、NSArray與NSSet的區(qū)別谢翎? NSArray內存中存儲地址連續(xù),而NSSet不連續(xù)NSSet效率高沐旨,內部...
    羈擁_f357閱讀 504評論 0 1
  • 一森逮、基礎知識點 1.設計模式是什么? 你知道哪些設計模式磁携,并簡要敘述褒侧? 設計模式是一種編碼經驗,就是用比較成熟的邏...
    iOS泡泡閱讀 235評論 0 1
  • 16宿命:用概率思維提高你的勝算 以前的我是風險厭惡者,不喜歡去冒險闷供,但是人生放棄了冒險烟央,也就放棄了無數的可能。 ...
    yichen大刀閱讀 6,054評論 0 4
  • 公元:2019年11月28日19時42分農歷:二零一九年 十一月 初三日 戌時干支:己亥乙亥己巳甲戌當月節(jié)氣:立冬...
    石放閱讀 6,883評論 0 2