2024 iOS面試知識(shí)匯總

說(shuō)在前面:
許久沒(méi)更新,最近整理就文件的時(shí)候,突然翻到兩年前換工作時(shí)整理的思維導(dǎo)圖,包含了原理八股文,網(wǎng)絡(luò),算法,以及架構(gòu),重構(gòu)解決方案等,基本上面試必問(wèn)的一些項(xiàng)目以及原理都包含在內(nèi)了。當(dāng)然了這里整理的很多內(nèi)容都只是幫助當(dāng)時(shí)的我回憶知識(shí)脈絡(luò),并沒(méi)有深入說(shuō)明,還需要讀者自己去查閱資料深入理解。希望能也幫助到最近準(zhǔn)備換工作的小伙伴梳理匯總知識(shí)點(diǎn)。思維導(dǎo)圖高清原圖下載地址
歡迎關(guān)注一下我的 Github: https://github.com/CYXiang
后續(xù)會(huì)更新一些AI、Flutter等相關(guān)文章。

思維導(dǎo)圖梳理

iOS原生相關(guān)

Objective-C 底層原理

  • OC對(duì)象

    • NSObject本質(zhì)

      • NSObject底層是結(jié)構(gòu)體,有一個(gè) Class isa 指針
      • 創(chuàng)建一個(gè)NSObject對(duì)象系統(tǒng)分配16字節(jié)(至少16),只使用了8字節(jié)(用于存放isa),objc源碼
    • 復(fù)雜對(duì)象本質(zhì)

      • 包含父類(lèi)成員結(jié)構(gòu)+子類(lèi)成員屬性

        • 內(nèi)存對(duì)齊,結(jié)構(gòu)體的大小必須使最大成員變量的倍數(shù)
    • 屬性和方法

      • 實(shí)例對(duì)象存放成員變量,不存放方法(存一份在類(lèi)對(duì)象就夠了)
    • 內(nèi)存分配注意點(diǎn)

      • 內(nèi)存分配方式內(nèi)存對(duì)齊、桶、堆內(nèi)存、16的倍數(shù)。目的是內(nèi)存優(yōu)化(gnu 內(nèi)存對(duì)齊)<sizeof(a) 是個(gè)運(yùn)算符,編譯就確定了,不是函數(shù)>
    • 對(duì)象分類(lèi)三類(lèi)

      • instance 實(shí)例對(duì)象

        • isa指針、成員變量值
      • class 類(lèi)對(duì)象

        • isa指針、superclass指針、類(lèi)的屬性信息(@property)、類(lèi)的對(duì)象方法信息(instance method)、類(lèi)的協(xié)議信息(protocol)、類(lèi)的成員變量信息(ivar)
      • meta-class 元類(lèi)對(duì)象

        • object_getclass([NSObject class]);
        • 與class類(lèi)對(duì)象結(jié)構(gòu)體一樣
        • isa指針、superclass指針、類(lèi)的類(lèi)方法信息(class method)
    • isa指向哪里?

      • instance的isa指向class、class的isa指向meta-class、meta-class的isa指向基類(lèi)的meta-class
      • 調(diào)用對(duì)象方法軌跡:isa找到class,方法不存在,就通過(guò)superclass找父類(lèi)的方法,再不存在就找基類(lèi)..
    • superclass指向哪里?

      • class的superclass指向父類(lèi)的class(如果沒(méi)有父類(lèi),superclass指針為null)、meta-class的superclass指向父類(lèi)的meta-class(基類(lèi)的meta-class的superclass指向基類(lèi)的class)
      • 調(diào)用類(lèi)方法軌跡:isa找meta-class,方法不存在,就通過(guò)superclass找父類(lèi)
    • OC的類(lèi)信息存放在哪里?

      • 對(duì)象方法、屬性、成員變量、協(xié)議信息,存放在class對(duì)象中
      • 類(lèi)方法,存放在meta-class對(duì)象中
      • 成員變量的具體值存放在instance對(duì)象中
  • KVO

    • 本質(zhì)是什么?

      • 利用Runtime動(dòng)態(tài)生成一個(gè)子類(lèi),讓instance對(duì)象的isa指向這個(gè)子類(lèi)NSKVONotifying_XXX
      • 當(dāng)修改instance對(duì)象的屬性時(shí),會(huì)調(diào)用Foundation的_NSSetXXXValueAndNotify函數(shù)
      • _NSSetXXXValueAndNotify函數(shù)內(nèi)部觸發(fā)監(jiān)聽(tīng)器的監(jiān)聽(tīng)方法
    • 如何手動(dòng)觸發(fā)KVO?

      • 手動(dòng)調(diào)用willChangeValueForKey: 和 didChangeValueForKey:
  • KVC

    • 1、KVC賦值會(huì)觸發(fā)KVO嗎?

      • 會(huì)!
    • 賦值原理

      • 賦值順序:①-(void)setKey; ②-(void)_setKey; 查看accessInstanceVariablesDirectly> ③_key; ④_isKey; ⑤key; ⑥isKey
    • 取值原理

      • 取值順序:① getKey、key、 isKey、_key方法 ②查看accessInstanceVariablesDirectly ③按照
        _key、_isKey、key、isKey順序查找成員變量
  • Cateogry

    • 通過(guò)runtime動(dòng)態(tài)將分類(lèi)合并到類(lèi)/元類(lèi)對(duì)象中

    • 實(shí)現(xiàn)原理

      • 1、通過(guò)Runtime加載某個(gè)類(lèi)所有的Category數(shù)據(jù)
      • 2、把所有Category的方法、屬性、協(xié)議數(shù)據(jù),合并到一個(gè)大數(shù)組中(后面參與編譯的Category數(shù)據(jù),會(huì)在數(shù)組的前面)
      • 3、將合并后的分類(lèi)數(shù)據(jù)(方法、屬性、協(xié)議),插入到類(lèi)原來(lái)的數(shù)據(jù)前面
    • Category與Class Extension的區(qū)別?

      • Class Extension在編譯時(shí)數(shù)據(jù)就包含在類(lèi)信息中
      • Category是在運(yùn)行時(shí)才會(huì)將數(shù)據(jù)合并到類(lèi)信息中
    • +load方法

      • 在runtime加載類(lèi)、分類(lèi)時(shí)調(diào)用,在程序運(yùn)行過(guò)程中只調(diào)用一次

      • 調(diào)用順序

        • 1、先調(diào)用類(lèi)的+load。(按照編譯先后順序調(diào)用,先編譯先調(diào)用。調(diào)用子類(lèi)的+load之前會(huì)先調(diào)用父類(lèi)的+load)
        • 2、在調(diào)用分類(lèi)的+load。(按照編譯先后順序調(diào)用,先編譯先調(diào)用)
    • +initialize方法

      • +initialize方法會(huì)在類(lèi)第一次接收到消息時(shí)調(diào)用

      • 調(diào)用順序

        • 先調(diào)用父類(lèi)的+initialize,再調(diào)用子類(lèi)的+initialize(先初始化父類(lèi),再初始化子類(lèi),每個(gè)類(lèi)只會(huì)初始化一次)
  • 關(guān)聯(lián)對(duì)象

    • 原理

      • 1、關(guān)聯(lián)對(duì)象并不是存儲(chǔ)在關(guān)聯(lián)對(duì)象本身內(nèi)存中
      • 2、關(guān)聯(lián)對(duì)象存儲(chǔ)在全局統(tǒng)一的一個(gè)AssociationManage中
      • 3、設(shè)置關(guān)聯(lián)對(duì)象為null,就是移除關(guān)聯(lián)對(duì)象
  • block

    • 原理/本質(zhì)

      • 本質(zhì)是OC對(duì)象,內(nèi)部也有個(gè)isa指針
      • 封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對(duì)象
    • block的類(lèi)型(三種,內(nèi)存存儲(chǔ)區(qū)域不同)

      • NSGlobalBlock

        • 數(shù)據(jù)區(qū)域Data區(qū)(與全局變量一起)
      • NSStackBlock

        • 棧(需要手動(dòng)銷(xiāo)毀)
      • NSMallocBlock

        • 堆(自動(dòng)銷(xiāo)毀)
    • block的變量捕獲(capture)

      • 為了保證block內(nèi)部能夠正常訪問(wèn)外部變量,有個(gè)變量捕獲機(jī)制

        • 局部變量(捕獲?。?/p>

          • auto類(lèi)型(自動(dòng)變量,離開(kāi)作用域就銷(xiāo)毀):值傳遞
          • static類(lèi)型(還能訪問(wèn)內(nèi)存):指針傳遞
        • 全局變量(不捕獲?。?/p>

          • 直接訪問(wèn)
    • __block修飾符

      • 用于解決修改block內(nèi)部無(wú)法修改auto變量值的問(wèn)題(不能修飾全局變量、靜態(tài)變量 static)

        • 編譯器會(huì)把__block變量包裝成一個(gè)對(duì)象
    • __block內(nèi)存管理

      • 1、當(dāng)block在棧上時(shí),并不會(huì)對(duì)__block變量產(chǎn)生強(qiáng)引用

      • 2、當(dāng)block被copy到堆時(shí)

        • 會(huì)調(diào)用block內(nèi)部的copy函數(shù)
        • copy函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_assign函數(shù)
        • _Block_object_assign函數(shù)會(huì)對(duì)__block變量形成強(qiáng)引用(retain)
    • block循環(huán)引用問(wèn)題?

  • Runtime

    • objc_msgSend執(zhí)行流程三大階段

      • 1、消息發(fā)送

        • 先找自身緩存,自身緩存找不到找父類(lèi)方法緩存找,找不到在父類(lèi)方法列表查找,以此類(lèi)推,找到就緩存到自身緩存中。找不到進(jìn)入階段2 ↓
      • 2、動(dòng)態(tài)方法解析

        • ①調(diào)用+resolvenInstanceMethod: 或者+resolvenClassMethod: 方法來(lái)動(dòng)態(tài)解析 ,進(jìn)行動(dòng)態(tài)添加方法
        • ②標(biāo)記為已經(jīng)動(dòng)態(tài)解析 YES
        • ③回到階段1、消息發(fā)送,因?yàn)橐呀?jīng)動(dòng)態(tài)加了方法且已標(biāo)記為YES(如果沒(méi)有添加,進(jìn)入階段3)
      • 3、消息轉(zhuǎn)發(fā)

        • ①調(diào)用(id)forwordingTargetForSelector:(SEL)aSelector 返回轉(zhuǎn)發(fā)對(duì)象,(返回nil就走下一步)

        • ②-返回方法簽名(返回nil就報(bào)錯(cuò),不為空就→)

          • 不為空調(diào)用 -(void)forwardInvocation:(NSInvocation *)anInvocation
  • RunLoop

    • 三種模式

      • NSDefalultRunLoopMode

      • UITrackingRunLoopMode

      • NSRunLoopCommonMode

        • 并不是一個(gè)真的模式
    • 運(yùn)行邏輯

      • 1、通知Observers:進(jìn)入Loop;通知Observers:即將處理Timers;通知Observers: 即將處理Sources
      • 2、處理Block;處理Source0(可能再次處理Block)
      • 3、如果存在Source1,跳轉(zhuǎn)到5
      • 4、通知Observers,開(kāi)始休眠(等待消息喚醒)
      • 5、通知Observers,結(jié)束休眠(被某個(gè)消息喚醒)①處理Timer ②處理GCD ③處理Source1
      • 6、根據(jù)前面的執(zhí)行結(jié)果決定如何操作
    • NSTimer失效問(wèn)題

      • NSTimer在默認(rèn)模式下,切換到NSRunLoopCommonMode
    • 線程?;?/p>

      • 1、添加Source; addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode]
      • 2、加while(weakSelf&&!weakSelf.isStopped) 循環(huán), 執(zhí)行currentRunLoop runMode:beforeDate:
    • 利用RunLoop監(jiān)控卡頓

      • 怎么才算卡頓?

        • 1、進(jìn)入睡眠前方法執(zhí)行時(shí)間過(guò)長(zhǎng)而導(dǎo)致無(wú)法進(jìn)入睡眠
        • 2、線程喚醒后接收消息時(shí)間過(guò)長(zhǎng)爾無(wú)法進(jìn)入下一步
      • 如何監(jiān)控卡頓?

        • 關(guān)注兩個(gè)階段(進(jìn)入睡眠之前和喚醒之后的兩個(gè)loop狀態(tài)定義的值)

          • 1、kCFRunLoopBeforeSources

            • 觸發(fā)Source0回調(diào)
          • 2、kCFRunLoopAfterWaiting

            • 接收mach_port消息
      • 如何監(jiān)聽(tīng)

        • 1、創(chuàng)建一個(gè)CFRunLoopObserverContext觀察者
        • 2、將觀察者添加到主線程RunLoop的Common模式下觀察。
        • 3、再創(chuàng)建一個(gè)持續(xù)的子線程專(zhuān)門(mén)用來(lái)監(jiān)控主線程的RunLoop狀態(tài)
        • 4、一旦發(fā)現(xiàn)進(jìn)入睡眠前的kCFRunLoopBeforeSource狀態(tài)或喚醒后的kCFRunLoopAfterWaiting,在設(shè)置的時(shí)間閾值內(nèi)一直沒(méi)有變化,即可判斷為卡頓,再dump出堆棧信息
  • 多線程

    • pthread

    • NSThread

    • GCD

      • 同步 sync

        • 沒(méi)有開(kāi)啟線程,串行執(zhí)行任務(wù)
      • 異步 async

        • 并且是并發(fā)隊(duì)列,才會(huì)開(kāi)啟新線程并發(fā)執(zhí)行任務(wù)
      • Semaphore信號(hào)量

        • 控制最大并發(fā)數(shù)量,也可用來(lái)線程同步(把信號(hào)量設(shè) 1 )
    • 死鎖

      • ①使用sync函數(shù)
      • ②往當(dāng)前串行隊(duì)列中添加任務(wù)
    • 隊(duì)列組

    • NSOperation

      • 封裝GCD
    • iOS線程同步方案(加鎖、GCD串行隊(duì)列)

      • OSSpinLock 自旋鎖

        • (不安全了不建議使用,優(yōu)先反轉(zhuǎn)問(wèn)題)
      • os_unfair_lock

        • OSSpinLock的替代方案
      • pthread_mutex

        • pthread_mutex_signal 激活一個(gè)等待該條件的線程
      • dispatch_semphore

        • 信號(hào)量
      • dispatch_queue(DISPATCH_QUEUE_SERIAL)

        • GCD串行隊(duì)列
      • pthread_mutex 的OC封裝

        • NSLock

        • NSRecursiveLock

          • 遞歸鎖,保證能夠遞歸調(diào)用
        • NSCondition

        • NSConditionLock

          • 帶條件的lock(生產(chǎn)者消費(fèi)者模式)
      • @synchronized

        • 性能最差
    • 讀寫(xiě)安全方案

      • 1、多讀單寫(xiě)(異步讀,同步寫(xiě)),用于文件數(shù)據(jù)讀寫(xiě)操作

        • pthread_rwlock 讀寫(xiě)鎖

          • 互斥鎖,等待鎖的過(guò)程會(huì)進(jìn)入休眠
        • dispatch_barrier_async 異步柵欄調(diào)用

          • 傳入的并發(fā)隊(duì)列必須是dispatch_queue_create創(chuàng)建的(不是就沒(méi)效果)
          • 如果傳入的是一個(gè)串行或全局并發(fā)隊(duì)列,那這個(gè)函數(shù)等同于dispatch_async效果
  • 內(nèi)存管理

    • 定時(shí)器

      • GCD是最準(zhǔn)確的,與RunLoop無(wú)關(guān)
    • 內(nèi)存布局

      • 保留內(nèi)存

      • 代碼段(__TEXT)

        • 編譯后的代碼
      • 數(shù)據(jù)段(__DATA)

        • 字符串常量
        • 已初始化數(shù)據(jù)
        • 未初始化數(shù)據(jù)
      • 堆(heap)

        • 通過(guò)alloc、malloc、calloc等動(dòng)態(tài)分配的空間
      • 棧(stack)

        • 函數(shù)調(diào)用開(kāi)銷(xiāo),局部變量的開(kāi)銷(xiāo)
      • 內(nèi)核區(qū)

    • Tagged Pointer

    • MRC

    • copy

    • weak 原理

      • 將弱引用存入到哈希表內(nèi),當(dāng)對(duì)象銷(xiāo)毀時(shí)就從表中取出弱引用并清除 (運(yùn)行時(shí)操作)
    • ARC 原理

      • 利用LLVM+Runtime,LLVM自動(dòng)生成插入retain,release代碼
    • autoRelease

UI層基本原理

  • 事件傳遞機(jī)制

    • 當(dāng)一個(gè)事件發(fā)生后,事件會(huì)從父控件傳給子控件,也就是說(shuō)由UIApplication -> UIWindow -> UIView -> initial view,以上就是事件的傳遞,也就是尋找最合適的view的過(guò)程
    • 事件的傳遞是從上到下(父控件到子控件),事件的響應(yīng)是從下到上(順著響應(yīng)者鏈條向上傳遞:子控件到父控件
    • 攔截事件 hitTest:withEvent:
  • UI繪制原理

    • 在layer內(nèi)部會(huì)創(chuàng)建一個(gè)backing store,我們可以理解為CGContextRef上下文。

    • 判斷l(xiāng)ayer是否有delegate:

      • 如果有delegate,則會(huì)執(zhí)行[layer.delegate drawLayer:inContext](這個(gè)方法的執(zhí)行是在系統(tǒng)內(nèi)部執(zhí)行的),然后在這個(gè)方法中會(huì)調(diào)用view的drawRect:方法,也就是我們重寫(xiě)view的drawRect:方法才會(huì)被調(diào)用到。
      • 如果沒(méi)有delegate,會(huì)調(diào)用layer的drawInContext方法,也就是我們可以重寫(xiě)的layer的該方法,此刻會(huì)被調(diào)用到。
    • 最后都由CALayer把繪制完的backing store(可以理解為位圖)提交給GPU。

  • 異步繪制原理

    • [UIView setNeedsDisplay]方法的時(shí)候,不會(huì)立馬發(fā)送對(duì)應(yīng)視圖的繪制工作

      • 調(diào)用[UIView setNeedsDisplay]后
      • 然后會(huì)調(diào)用系統(tǒng)的同名方法[view.layer setNeedsDisplay]方法并在當(dāng)前view上面打上一個(gè)臟標(biāo)記
      • 當(dāng)前Runloop將要結(jié)束的時(shí)候才會(huì)調(diào)用[CALyer display]方法,然后進(jìn)入到視圖真正的繪制工作當(dāng)中
    • 是否知道異步繪制?如何進(jìn)行異步繪制?

      • 基于系統(tǒng)開(kāi)的口子[layer.delegate dispayLayer:]方法
      • 并且實(shí)現(xiàn)/遵從了dispayLayer這個(gè)方法,我們就可以進(jìn)行異步繪制
  • 離屏渲染

    • 觸發(fā)離屏渲染的場(chǎng)景

      • 采用了光柵化的 layer (layer.shouldRasterize)
      • 使用了 mask 的 layer (layer.mask)
      • 需要進(jìn)行裁剪的 layer
      • 設(shè)置了組透明度為 YES,并且透明度不為 1 的layer
      • 高斯模糊
      • 添加了投影的 layer (layer.shadow*)
      • 繪制了文字的 layer (UILabel, CATextLayer, Core Text 等
    • 使用Instruments的不同工具來(lái)測(cè)試性能

LLDB

  • 動(dòng)態(tài)調(diào)試

    • 子主題 1

Clang + LLVM

  • Clang編譯步驟

    • 1、預(yù)處理

      • 頭文件替換,宏替換,預(yù)編譯指令替換
    • 2、詞法分析

      • 輸出token流
    • 3、語(yǔ)法分析

      • 生成AST抽象語(yǔ)法樹(shù)
    • 4、CodeGen

      • CodeGen 負(fù)責(zé)將語(yǔ)法樹(shù)叢頂至下遍歷,翻譯成LLVM IR

      • 生成中間代碼IR,與RunTime橋接

        • ARC:分析對(duì)象引用關(guān)系,將objc_storeStrong/objc_storeWeak等ARC代碼插入
        • 根據(jù)修飾符strong/weak/copy/atomic合成@property 自動(dòng)實(shí)現(xiàn)的 setter/getter
  • LLVM后端

    • 優(yōu)化 IR
    • LLVM BitCode字節(jié)碼
  • 如何使用Clang做靜態(tài)分析

    • OCLint

      • 基本覆蓋了具有通用性的規(guī)則,主要包括語(yǔ)法上的基礎(chǔ)規(guī)則、Cocoa 庫(kù)相關(guān)規(guī)則、一些約定俗成的規(guī)則、各種空語(yǔ)句檢查、是否按新語(yǔ)法改寫(xiě)的檢查、命名上長(zhǎng)變量名短變量名檢查、無(wú)用的語(yǔ)句變量和參數(shù)的檢查
    • Clang 靜態(tài)分析器

      • scan-build 是用來(lái)運(yùn)行分析器的命令行工具
    • Infer(Facebook 開(kāi)源的、使用 OCaml 語(yǔ)言編寫(xiě))

      • 空指針訪問(wèn)
      • 資源泄露
      • 內(nèi)存泄露

iOS簽名機(jī)制

  • 保證安裝到用戶(hù)手機(jī)上的APP都是經(jīng)過(guò)Apple官方允許的
  • 生成CertificateSigningRequest.certSigningRequest文件

iOS安全

  • 代碼混淆

    • 源碼的混淆

      • 類(lèi)名
      • 方法名
      • 協(xié)議名
  • 字符串加密

越獄相關(guān)

  • 越獄檢測(cè)

Swift

性能優(yōu)化

CPU與GPU

  • CPU

    • 對(duì)象的創(chuàng)建銷(xiāo)毀、對(duì)象屬性的調(diào)整、布局計(jì)算、文本計(jì)算和排版、圖片的格式轉(zhuǎn)換和解碼、圖像的繪制(Core Graphics)
  • GPU

    • 紋理的渲染、

啟動(dòng)優(yōu)化

  • 啟動(dòng)速度監(jiān)控

    • 1、定時(shí)抓取主線程上方法調(diào)用堆棧,計(jì)算一段時(shí)間里各個(gè)方法的耗時(shí)(Xcode 工具套件里自帶的 Time Profiler ,采用的就是這種方式)

    • 2、對(duì)objc_msgSend方法進(jìn)行hook

      • 原理解釋
      • 如何使用
  • 方案與實(shí)踐

    • iOS冷啟動(dòng)階段思路

      • System Interface

        加載主二進(jìn)制、啟動(dòng)dyld、加載動(dòng)態(tài)庫(kù)以及l(fā)ibSystem初始化

        • 避免鏈接不使用的框架
        • 減少動(dòng)態(tài)庫(kù)的加載
        • 減少OC類(lèi),分類(lèi)等
      • runtime Init

        執(zhí)行 +load和staic initializer初始化函數(shù)等

        • 避免在+load里操作
        • 延遲加載+load
        • 減少staic initializer(C++ 靜態(tài)全局變量)
      • UIKit init

      • Application init

        實(shí)例化UI Application 和 UI Application Delegate、處理生命周期回調(diào)、首幀渲染直到首頁(yè)渲染完成

        • 減少或延遲各種SDK的初始化
      • initial Frame Render

        • 減少視圖層級(jí)和視圖數(shù)量
        • 懶加載View
        • 變AutoLayout為手動(dòng)frame布局等
      • Extended

        • 去掉viewDidLoad和viewWillAppear中不必要的邏輯,少做事不做事
    • 方案

      • 低成本高收益方案

        • 生命周期延遲
        • +load治理
        • 動(dòng)態(tài)庫(kù)下線
        • 二進(jìn)制重排
        • 首頁(yè)預(yù)加載
      • 深入優(yōu)化方案

        • 動(dòng)態(tài)庫(kù)懶加載
        • staic initlializer治理
        • 編譯期寫(xiě)入I/O
        • 任務(wù)編排
  • 流程規(guī)范與監(jiān)控

    • 規(guī)范

      • 1、新增或修改任務(wù)要有足夠的理由,必須經(jīng)過(guò)嚴(yán)格的code review
      • 2、首頁(yè)渲染完成前不允許監(jiān)聽(tīng)生命周期
      • 3、不允許新增+load耗時(shí)方法
      • 4、不允許新增C++ initialize
      • 5、新增動(dòng)態(tài)庫(kù)必須經(jīng)過(guò)評(píng)估
      • 6、任務(wù)項(xiàng)相對(duì)上個(gè)版本有5ms以上的增長(zhǎng)時(shí),必須進(jìn)行修改
    • 監(jiān)控

卡頓排查與解決

  • UI界面卡頓優(yōu)化(滑動(dòng)掉幀等)

    • 盡量減少CPU GPU資源消耗
  • 崩潰類(lèi)型的卡頓排查(線程卡頓)

耗電排查與解決

  • 1、如何獲取電量

    • (1)引入 IOPowerSources.h、IOPSKeys.h 和 IOKit
    • (2)把 batteryMonitoringEnabled 置為 true
  • 2、如何診斷電量問(wèn)題

    • (1)通過(guò) task_threads 函數(shù),獲取所有的線程信息數(shù)組 threads以及線程總數(shù) threadCount
    • (2)thread_basic_info 里有一個(gè)記錄 CPU 使用百分比的字段 cpu_usage
    • (3)遍歷所有線程,去查看是哪個(gè)線程的 CPU 使用百分比過(guò)高。(某個(gè)線程的 CPU 使用率長(zhǎng)時(shí)間都比較高,可能有問(wèn)題)
  • 3、如何優(yōu)化電量

    • (1)避免讓 CPU 做多余的事情。對(duì)于大量數(shù)據(jù)的復(fù)雜計(jì)算,應(yīng)該把數(shù)據(jù)傳到服務(wù)器去處理

      必須要在 App 內(nèi)處理復(fù)雜數(shù)據(jù)計(jì)算,可以通過(guò) GCD 的 dispatch_block_create_with_qos_class 方法指定隊(duì)列的 Qos 為 QOS_CLASS_UTILITY,將計(jì)算工作放到這個(gè)隊(duì)列的 block 里。在 QOS_CLASS_UTILITY 這種 Qos 模式下,系統(tǒng)針對(duì)大量數(shù)據(jù)的計(jì)算,以及復(fù)雜數(shù)據(jù)處理專(zhuān)門(mén)做了電量?jī)?yōu)化。

    • (2)I/O 操作也是耗電大戶(hù),優(yōu)化I/O操作

      業(yè)內(nèi)的普遍做法是,將碎片化的數(shù)據(jù)磁盤(pán)存儲(chǔ)操作延后,先在內(nèi)存中聚合,然后再進(jìn)行磁盤(pán)存儲(chǔ)。碎片化的數(shù)據(jù)進(jìn)行聚合,在內(nèi)存中進(jìn)行存儲(chǔ)的機(jī)制,可以使用系統(tǒng)自帶的 NSCache 來(lái)完成。

      NSCache 是線程安全的,NSCache 會(huì)在到達(dá)預(yù)設(shè)緩存空間值時(shí)清理緩存,這時(shí)會(huì)觸發(fā) cache:willEvictObject: 方法的回調(diào),在這個(gè)回調(diào)里就可以對(duì)數(shù)據(jù)進(jìn)行 I/O 操作,達(dá)到將聚合的數(shù)據(jù) I/O 延后的目的。I/O 操作的次數(shù)減少了,對(duì)電量的消耗也就減少了

    • (3)蘋(píng)果維護(hù)了一個(gè)電量?jī)?yōu)化指南“Energy Efficiency Guide for iOS Apps”

包瘦身

  • 官方 App Thinning

  • 圖片資源優(yōu)化

    • 無(wú)用圖片移除,圖片壓縮

      • LSUnusedResources
    • TinyPng或者ImageOptim、轉(zhuǎn)webp

  • 代碼瘦身

    • 刪除無(wú)用功能代碼(A/B測(cè)試結(jié)果刪除)

    • 源代碼瘦身

      • LinkMap 結(jié)合 Mach-O 找無(wú)用代碼

        • AppCode(人工二次確認(rèn))
      • 運(yùn)行時(shí)檢查類(lèi)是否真正被使用過(guò)

        • 通過(guò)isInitialized,判斷一個(gè)類(lèi)是否初始化過(guò)
    • 重復(fù)代碼刪除(Clang插件)

  • 可執(zhí)行文件瘦身

    • 編譯器優(yōu)化

架構(gòu)設(shè)計(jì)

組件化

  • 協(xié)議式

    • 協(xié)議式架構(gòu)設(shè)計(jì)主要采用的是協(xié)議式編程的思路

      • 在編譯層面使用協(xié)議定義規(guī)范,實(shí)現(xiàn)可在不同地方,從而達(dá)到分布管理和維護(hù)組件的目的
    • 缺陷

      • 協(xié)議式編程缺少統(tǒng)一調(diào)度層,導(dǎo)致難于集中管理
      • 協(xié)議式編程接口定義模式過(guò)于規(guī)范,從而使得架構(gòu)的靈活性不夠高。當(dāng)需要引入一個(gè)新的設(shè)計(jì)模式來(lái)開(kāi)發(fā)時(shí),我們就會(huì)發(fā)現(xiàn)很難融入到當(dāng)前架構(gòu)中,缺乏架構(gòu)的統(tǒng)一性。
  • 中間者

    • 優(yōu)勢(shì)

      • 拆分的組件都會(huì)依賴(lài)于中間者,但是組間之間就不存在相互依賴(lài)的關(guān)系
      • 其他組件都會(huì)依賴(lài)于這個(gè)中間者,相互間的通信都會(huì)通過(guò)中間者統(tǒng)一調(diào)度
      • 在中間者上也能夠輕松添加新的設(shè)計(jì)模式,從而使得架構(gòu)更容易擴(kuò)展
      • 中間者架構(gòu)的易管控帶來(lái)的架構(gòu)更穩(wěn)固,易擴(kuò)展帶來(lái)的靈活性
    • 實(shí)現(xiàn)方案

      • CTMediator

        • CTMediator 本質(zhì)就是一個(gè)方法,用來(lái)接收 target、action、params,對(duì)于調(diào)用者來(lái)說(shuō)十分不友好

          • 通過(guò)響應(yīng)者給 CTMediator 做的 category 或者 extension 發(fā)起調(diào)用
          • category 或 extension 以函數(shù)聲明的方式,解決了參數(shù)的問(wèn)題
          • 不會(huì)直接依賴(lài) CTMediator 去發(fā)起調(diào)用,而是直接依賴(lài) category Pod 去發(fā)起調(diào)用
        • 解耦的精髓在于業(yè)務(wù)邏輯能夠獨(dú)立出來(lái),并不是形式上的解除編譯上的耦合(編譯上解除耦合只能算是解耦的一種手段而已)。更多的還是需要在功能邏輯和組件劃分上做到同層級(jí)解耦,上下層依賴(lài)清晰

      • URLRoutor

        • 缺陷

          • 本地間調(diào)用無(wú)法傳遞非常規(guī)參數(shù),復(fù)雜參數(shù)的傳遞方式非常丑陋
          • 必須要在app啟動(dòng)時(shí)注冊(cè)URL響應(yīng)者
          • 新增組件化的調(diào)用路徑時(shí),蘑菇街的操作相對(duì)復(fù)雜

MVC

MVVM

  • 雙向綁定(RAC,RSSwift)

設(shè)計(jì)模式

系統(tǒng)化思維

五大設(shè)計(jì)原則

  • 單一功能原則:對(duì)象功能要單一,不要在一個(gè)對(duì)象里添加很多功能
  • 開(kāi)閉原則:擴(kuò)展是開(kāi)放的,修改是封閉的
  • 里氏替換原則:子類(lèi)對(duì)象是可以替代基類(lèi)對(duì)象的
  • 接口隔離原則:接口的用途要單一,不要在一個(gè)接口上根據(jù)不同入?yún)?shí)現(xiàn)多個(gè)功能
  • 依賴(lài)反轉(zhuǎn)原則:方法應(yīng)該依賴(lài)抽象,不要依賴(lài)實(shí)例。iOS 開(kāi)發(fā)就是高層業(yè)務(wù)方法依賴(lài)于協(xié)議

23種設(shè)計(jì)模式實(shí)現(xiàn)原理

  • 子主題 1

網(wǎng)絡(luò)協(xié)議相關(guān)

IP層

TCP/UDP

  • TCP

    • TCP是一個(gè)傳輸層協(xié)議,提供端到端(Host-To-Host) 數(shù)據(jù)的可靠傳輸
    • 支持全雙工,是一個(gè)連接導(dǎo)向的協(xié)議(面向連接的)
  • UDP

    • 目標(biāo)是在傳輸層提供直接發(fā)送報(bào)文(Datagram)的能力

HTTP/HTTPS

  • 為什么可以相信一個(gè) HTTPS 網(wǎng)站?

    • 當(dāng)用戶(hù)用瀏覽器打開(kāi)一個(gè) HTTPS 網(wǎng)站時(shí),會(huì)到目標(biāo)網(wǎng)站下載目標(biāo)網(wǎng)站的證書(shū)
    • 瀏覽器會(huì)去驗(yàn)證證書(shū)上的簽名,一直驗(yàn)證到根證書(shū),如果根證書(shū)被預(yù)裝,那么就會(huì)信任這個(gè)網(wǎng)站

DNS

Socket

  • Socket 是一種編程的模型

    • 客戶(hù)端將數(shù)據(jù)發(fā)送給在客戶(hù)端側(cè)的Socket 對(duì)象,然后客戶(hù)端側(cè)的 Socket 對(duì)象將數(shù)據(jù)發(fā)送給服務(wù)端側(cè)的 Socket 對(duì)象
    • Socket 對(duì)象負(fù)責(zé)提供通信能力,并處理底層的 TCP 連接/UDP 連接
    • 對(duì)服務(wù)端而言,每一個(gè)客戶(hù)端接入,就會(huì)形成一個(gè)和客戶(hù)端對(duì)應(yīng)的 Socket 對(duì)象,如果服務(wù)器要讀取客戶(hù)端發(fā)送的信息,或者向客戶(hù)端發(fā)送信息,就需要通過(guò)這個(gè)客戶(hù)端 Socket 對(duì)象
  • Socket 還是一種雙向管道文件

    • 操作系統(tǒng)將客戶(hù)端傳來(lái)的數(shù)據(jù)寫(xiě)入這個(gè)管道,也將線程寫(xiě)入管道的數(shù)據(jù)發(fā)送到客戶(hù)端

算法

數(shù)組&鏈表

堆棧&隊(duì)列

  • 面試題:【判斷字符串括號(hào)是否合法】

  • 單調(diào)棧

    • 遞增棧

      • 小數(shù)消除大數(shù)
    • 遞減棧

      • 大數(shù)消除小數(shù)
  • 優(yōu)先隊(duì)列

    • 正常進(jìn),安裝優(yōu)先級(jí)出
    • 實(shí)現(xiàn)機(jī)制:1、Heap(堆)(Binary、Binomial、Fiboncci)

哈希表

  • Map/Set

    • 【有效的字母異位詞】
    • 【兩數(shù)之和】
    • 【三數(shù)之和】

樹(shù)

  • 二叉樹(shù)

    • 反轉(zhuǎn)二叉樹(shù)

      • 遍歷二叉樹(shù)
  • 二叉搜索樹(shù)

  • 字典樹(shù)

遞歸&分治

動(dòng)態(tài)規(guī)劃

  • 貪心算法
  • 買(mǎi)賣(mài)股票
  • 背包問(wèn)題

LRU Cache

Bloom Filter(布隆過(guò)濾器)

斐波那契數(shù)列

Flutter

底層基本實(shí)現(xiàn)

Bloc與響應(yīng)式

容器化&配置化

組件化

性能優(yōu)化與實(shí)踐

安全與密碼學(xué)

單向散列函數(shù)(哈希函數(shù))

  • SHA-1、MD5(已不安全)
  • SHA-256、SHA-384、SHA-512(目前流行)

加密算法

  • 對(duì)稱(chēng)加密

    • 序列算法(優(yōu)先使用)

      • ChaCha20、AES-256、AES-128
    • 分組算法

  • 非對(duì)稱(chēng)加密

亮點(diǎn)與疑難解決

動(dòng)態(tài)化

容器化

配置化

持續(xù)集成

fastlane

RunTime無(wú)埋點(diǎn)方案

產(chǎn)品主要想知道:頁(yè)面進(jìn)入次數(shù)、頁(yè)面停留時(shí)間、點(diǎn)擊事件的埋點(diǎn)(用來(lái)計(jì)算曝光率、轉(zhuǎn)化率)

運(yùn)行時(shí)方法替換方式進(jìn)行埋點(diǎn)(AOP)

  • 寫(xiě)一個(gè)運(yùn)行時(shí)方法替換的類(lèi) SMHook
  • 利用運(yùn)行時(shí)接口將方法的實(shí)現(xiàn)進(jìn)行了交換,原方法調(diào)用時(shí)就會(huì)被 hook 住,從而去執(zhí)行指定的方法
  • 每個(gè) UIViewController 生命周期到了 ViewWillAppear 時(shí)都會(huì)去執(zhí)行 insertToViewWillAppear 方法

事件唯一標(biāo)識(shí)區(qū)分不同埋點(diǎn)

  • NSStringFromClass([self class]) 方法來(lái)取類(lèi)名,區(qū)別不同的 UIViewController
  • action 選擇器名 NSStringFromSelector(action)” +“視圖類(lèi)名 NSStringFromClass([target class])”組合成一個(gè)唯一的標(biāo)識(shí)
  • 通過(guò)視圖的 superview 和 subviews 的屬性,我們就能夠還原出每個(gè)頁(yè)面的視圖樹(shù)
  • 復(fù)用機(jī)制 UITableViewCell 用 indexPath

RunLoop運(yùn)行步驟

1、通知 observers:RunLoop 要開(kāi)始進(jìn)入 loop 了。緊接著就進(jìn)入 loop

2、開(kāi)啟一個(gè) do while 來(lái)保活線程。通知 Observers:RunLoop 會(huì)觸發(fā) Timer 回調(diào)、Source0 回調(diào),接著執(zhí)行加入的 block

通知 Observers:RunLoop 的線程將進(jìn)入休眠(sleep)狀態(tài)

4、進(jìn)入休眠后,會(huì)等待 mach_port 的消息,以再次喚醒

5、喚醒時(shí)通知 Observer:RunLoop 的線程剛剛被喚醒了

6、RunLoop 被喚醒后就要開(kāi)始處理消息了

啟動(dòng)優(yōu)化方案

main() 函數(shù)執(zhí)行前

  • 加載可執(zhí)行文件(App 的.o 文件的集合)
  • 加載動(dòng)態(tài)鏈接庫(kù),進(jìn)行 rebase 指針調(diào)整和 bind 符號(hào)綁定
  • Objc 運(yùn)行時(shí)的初始處理,包括 Objc 相關(guān)類(lèi)的注冊(cè)、category 注冊(cè)、selector 唯一性檢查等
  • 初始化,包括了執(zhí)行 +load() 方法、attribute((constructor)) 修飾的函數(shù)的調(diào)用、創(chuàng)建 C++ 靜態(tài)全局變量

main() 函數(shù)執(zhí)行后(appDelegate 的 didFinishLaunchingWithOptions 方法里首屏渲染相關(guān)方法執(zhí)行完成)

  • 首屏初始化所需配置文件的讀寫(xiě)操作
  • 首屏列表大數(shù)據(jù)的讀取
  • 首屏渲染的大量計(jì)算等

首屏渲染完成后(加載時(shí)長(zhǎng))

  • 減少視圖層級(jí)和視圖數(shù)量
  • 懶加載View
  • 變AutoLayout為手動(dòng)frame布局等
  • 預(yù)加載(緩存首頁(yè),骨架屏等)
  • 去掉viewDidLoad和viewWillAppear中不必要的邏輯,少做事不做事

APM系統(tǒng)

啟動(dòng)優(yōu)化、卡頓監(jiān)聽(tīng)、崩潰監(jiān)聽(tīng)、性能監(jiān)控

GNUStep 源碼

LLD鏈接器

鏈接器最主要的作用,就是將符號(hào)綁定到地址上

使用 dyld 加載動(dòng)態(tài)庫(kù)

調(diào)用 +load 方法是通過(guò) runtime 庫(kù)處理的

Injection for Xcode 動(dòng)態(tài)調(diào)試

  • 1、Injection 會(huì)監(jiān)聽(tīng)源代碼文件的變化
  • 2、如果文件被改動(dòng)了,Injection Server 就會(huì)執(zhí)行 rebuildClass 重新進(jìn)行編譯、打包成動(dòng)態(tài)庫(kù),也就是 .dylib 文件
  • 3、編譯、打包成動(dòng)態(tài)庫(kù)后使用 writeSting 方法通過(guò) Socket 通知運(yùn)行的 App

更安全的方法交換庫(kù) Aspects

通過(guò) Runtime 消息轉(zhuǎn)發(fā)機(jī)制來(lái)實(shí)現(xiàn)方法交換的庫(kù)

它將所有的方法調(diào)用都指到 _objc_msgForward 函數(shù)調(diào)用上

按照自己的方式實(shí)現(xiàn)了消息轉(zhuǎn)發(fā),自己處理參數(shù)列表,處理返回值

最后通過(guò) NSInvocation 調(diào)用來(lái)實(shí)現(xiàn)方法交換

事件總線技術(shù) Promise

PromiseKit

通過(guò)簡(jiǎn)單、清晰、規(guī)范的 Promise 接口將異步的數(shù)據(jù)獲取、業(yè)務(wù)邏輯、界面串起來(lái),對(duì)于日后的維護(hù)或重構(gòu)都會(huì)容易很多

Source0與Source1

Source0

  • 不能主動(dòng)觸發(fā)事件
  • 使用時(shí),你需要先調(diào)用CFRunLoopSourceSignal,將這個(gè)Source標(biāo)記為待處理,然后手動(dòng)調(diào)用CFRunLoopWakeUp來(lái)喚醒RunLoop,讓其處理這個(gè)事件

Source1

  • 主動(dòng)觸發(fā)事件。其中它有一個(gè)mach_port_t

TCP 最核心的價(jià)值是提供了可靠性,而 UDP 最核心的價(jià)值是靈活

HTTP 協(xié)議 1.1 和 2.0 都基于 TCP,而到了 HTTP 3.0 就開(kāi)始用 UDP

TCP與UDP區(qū)別

目的差異

  • TCP 協(xié)議的核心目標(biāo)是提供可靠的網(wǎng)絡(luò)傳輸
  • UDP 的目標(biāo)是在提供報(bào)文交換能力基礎(chǔ)上盡可能地簡(jiǎn)化協(xié)議輕裝上陣

可靠性差異

  • TCP 核心是要在保證可靠性提供更好的服務(wù)。TCP 會(huì)有握手的過(guò)程,需要建立連接
  • UDP 并不具備以上這些特性,它只管發(fā)送數(shù)據(jù)封包,而且 UDP 不需要 ACK

連接 vs 無(wú)連接

  • TCP 是一個(gè)面向連接的協(xié)議,傳輸數(shù)據(jù)必須先建立連接
  • UDP 是一個(gè)無(wú)連接協(xié)議,數(shù)據(jù)隨時(shí)都可以發(fā)送,只提供發(fā)送封包(Datagram)的能力

傳輸速度

  • UDP 協(xié)議簡(jiǎn)化,封包小,沒(méi)有連接、可靠性檢查等,因此單純從傳輸速度上講,UDP 更快

場(chǎng)景差異

  • TCP 場(chǎng)景

    • 遠(yuǎn)程控制(SSH)

File Transfer Protocol(FTP)

郵件(SMTP、IMAP)等

點(diǎn)對(duì)點(diǎn)文件傳出(微信等)

  • UDP 場(chǎng)景

    • 網(wǎng)絡(luò)游戲

音視頻傳輸

DNS

Ping

直播

APP如何加載?

iOS系統(tǒng)架構(gòu)

  • 用戶(hù)體驗(yàn)層,主要是提供用戶(hù)界面。這一層包含了 SpringBoard、Spotlight、Accessibility
  • 第二層是應(yīng)用框架層,是開(kāi)發(fā)者會(huì)用到的。這一層包含了開(kāi)發(fā)框架 Cocoa Touch
  • 第三層是核心框架層,是系統(tǒng)核心功能的框架層。這一層包含了各種圖形和媒體核心框架、Metal 等
  • 第四層是 Darwin 層,是操作系統(tǒng)的核心,屬于操作系統(tǒng)的內(nèi)核態(tài)。這一層包含了系統(tǒng)內(nèi)核 XNU、驅(qū)動(dòng)等

XNU 怎么加載 App?

  • iOS 的可執(zhí)行文件和動(dòng)態(tài)庫(kù)都是 Mach-O 格式,所以加載 APP 實(shí)際上就是加載 Mach-O 文件
  • 加載 Mach-O 文件,內(nèi)核會(huì) fork 進(jìn)程,并對(duì)進(jìn)程進(jìn)行一些基本設(shè)置,比如為進(jìn)程分配虛擬內(nèi)存、為進(jìn)程創(chuàng)建主線程、代碼簽名等。用戶(hù)態(tài) dyld 會(huì)對(duì) Mach-O 文件做庫(kù)加載和符號(hào)解析

好架構(gòu)定義

高可用

高性能

易擴(kuò)展

在功能邏輯和組件劃分上做到同層級(jí)解耦,上下層依賴(lài)清晰,這樣的結(jié)構(gòu)才能夠使得上層組件易插拔,下層組件更穩(wěn)固

組件化架構(gòu)

1、業(yè)務(wù)完全解耦,通用功能下沉

組件

2、每個(gè)業(yè)務(wù)都是一個(gè)獨(dú)立的 Git 倉(cāng)庫(kù),每個(gè)業(yè)務(wù)都能夠生成一個(gè) Pod 庫(kù),最后再集成到一起

組件分層

  • 底層可以是與業(yè)務(wù)無(wú)關(guān)的基礎(chǔ)組件,比如網(wǎng)絡(luò)和存儲(chǔ)等
  • 中間層一般是通用的業(yè)務(wù)組件,比如賬號(hào)、埋點(diǎn)、支付、購(gòu)物車(chē)等
  • 最上層是迭代業(yè)務(wù)組件,更新頻率最高

多團(tuán)隊(duì)之間如何分工?

  • 基建團(tuán)隊(duì),負(fù)責(zé)業(yè)務(wù)無(wú)關(guān)的基礎(chǔ)功能組件和業(yè)務(wù)相關(guān)通用業(yè)務(wù)組件的開(kāi)發(fā)
  • 每個(gè)業(yè)務(wù)都由一個(gè)專(zhuān)門(mén)的團(tuán)隊(duì)來(lái)負(fù)責(zé)開(kāi)發(fā)
  • 基建團(tuán)隊(duì)人員應(yīng)該是流動(dòng)的,從業(yè)務(wù)團(tuán)隊(duì)里來(lái),再回到業(yè)務(wù)團(tuán)隊(duì)中去

監(jiān)控崩潰與采集

崩潰類(lèi)型

  • 信號(hào)可捕獲到

    • KVO、數(shù)組越界、返回類(lèi)型不匹配N(xiāo)ULL

    • 多線程問(wèn)題

      • 在子線程中進(jìn)行 UI 更新可能會(huì)發(fā)生崩潰。多個(gè)線程進(jìn)行數(shù)據(jù)的讀取操作,因?yàn)樘幚頃r(shí)機(jī)不一致,比如有一個(gè)線程在置空數(shù)據(jù)的同時(shí)另一個(gè)線程在讀取這個(gè)數(shù)據(jù),可能會(huì)出現(xiàn)崩潰情況
    • 野指針

      • 指針指向一個(gè)已刪除的對(duì)象訪問(wèn)內(nèi)存區(qū)域時(shí),會(huì)出現(xiàn)野指針崩潰
  • 信號(hào)不可捕獲

    • 后臺(tái)任務(wù)超時(shí)

      • iOS 后臺(tái)保活

        • Background Task (3 分鐘)

          • 系統(tǒng)提供了 beginBackgroundTaskWithExpirationHandler 方法來(lái)延長(zhǎng)后臺(tái)執(zhí)行時(shí)間,可以解決你退后臺(tái)后還需要一些時(shí)間去處理一些任務(wù)的訴求
    • 主線程卡頓超閾值

      • 主線程無(wú)響應(yīng)

        • 如果主線程超過(guò)系統(tǒng)規(guī)定的時(shí)間無(wú)響應(yīng),就會(huì)被 Watchdog 殺掉
    • 內(nèi)存打爆

      • JetSam 機(jī)制

        • 操作系統(tǒng)為了控制內(nèi)存資源過(guò)度使用而采用的一種資源管控機(jī)制
      • 通過(guò)內(nèi)存警告獲取內(nèi)存限制值

        • didReceiveMemoryWarning

          • 強(qiáng)殺掉 App 之前還有 6 秒鐘的時(shí)間
      • 定位內(nèi)存問(wèn)題信息收集

        • 誰(shuí)分配的內(nèi)存?定位到函數(shù)

          • 用 fishhook 去 Hook “malloc_logger”函數(shù),分析統(tǒng)計(jì)

監(jiān)控方案

  • 第三方的

    • Fabric或Bugly
  • 第三方開(kāi)源自建服務(wù)器

    • PLCrashReporter

      • 第三方開(kāi)源庫(kù)捕獲崩潰日志,然后上傳到自己服務(wù)器上進(jìn)行整體監(jiān)控的

A/B測(cè)試方案(SkyLab)

三個(gè)部分

  • 策略服務(wù),為策略制定者提供策略

    • 決策流程、策略維度
  • A/B 測(cè)試 SDK,集成在客戶(hù)端內(nèi)

    • 客戶(hù)端SDK:SkyLab

      • 使用的是 MMKV 保存策略

      • SkyLab 對(duì)外的調(diào)用接口使用的是 Block ,來(lái)接收版本 A 和 B 的區(qū)別處理。

      • 如何做人群測(cè)試桶劃分

        • 隨機(jī)分配方式,將分配結(jié)果通過(guò) MMKV 進(jìn)行持續(xù)化存儲(chǔ),確保測(cè)試桶的一致性
  • 日志系統(tǒng),負(fù)責(zé)反饋策略結(jié)果供分析人員分析不同策略執(zhí)行的結(jié)果

服務(wù)端返回A/B實(shí)驗(yàn)

性能監(jiān)控

線下性能

  • Energy Log 就是用來(lái)監(jiān)控耗電量的
  • Leaks 就是專(zhuān)門(mén)用來(lái)監(jiān)控內(nèi)存泄露問(wèn)題的
  • Network 就是用來(lái)專(zhuān)門(mén)檢查網(wǎng)絡(luò)情況
  • Time Profiler 就是通過(guò)時(shí)間采樣來(lái)分析頁(yè)面卡頓問(wèn)題

線上監(jiān)控(不要侵入到業(yè)務(wù)代碼、采用性能消耗最小的監(jiān)控方案)

  • CPU 使用率的線上監(jiān)控(App 作為進(jìn)程運(yùn)行起來(lái)后會(huì)有多個(gè)線程,每個(gè)線程對(duì) CPU 的使用率不同。各個(gè)線程對(duì) CPU 使用率的總和,就是當(dāng)前 App 對(duì) CPU 的使用率)

    • thread_info.h 根據(jù)當(dāng)前 task 獲取所有線程
    • 遍歷所有線程來(lái)獲取單個(gè)線程的基本信息
    • thread_basic_info 結(jié)構(gòu)體獲取CPU 使用率的字段:cpu_usage
    • 累加這個(gè)字段就能夠獲取到當(dāng)前的整體 CPU 使用率
  • FPS 線上監(jiān)控

    • 通過(guò)注冊(cè) CADisplayLink 得到屏幕的同步刷新率
    • 記錄每次刷新時(shí)間,然后就可以得到 FPS
  • 內(nèi)存使用量的線上監(jiān)控

    • 內(nèi)存信息存在 task_vm_info
    • 類(lèi)似于對(duì) CPU 使用率的監(jiān)控,我們只要從這個(gè)結(jié)構(gòu)體里取出 phys_footprint 字段

啟動(dòng)優(yōu)化監(jiān)控方案

定時(shí)抓取主線程上的方法調(diào)用堆棧,計(jì)算一段時(shí)間里各個(gè)方法的耗時(shí)(Xcode 工具套件里自帶的 Time Profiler ,采用的就是這種方式)

對(duì) objc_msgSend 方法進(jìn)行 hook 來(lái)掌握所有方法的執(zhí)行耗時(shí)

使用RunLoop監(jiān)控卡頓

原因

  • 復(fù)雜 UI 、圖文混排的繪制量過(guò)大
  • 在主線程上做網(wǎng)絡(luò)同步請(qǐng)求
  • 在主線程做大量的 IO 操作
  • 運(yùn)算量過(guò)大,CPU 持續(xù)高占用
  • 死鎖和主子線程搶鎖

RunLoop基本原理

  • 用來(lái)監(jiān)聽(tīng)輸入源,進(jìn)行調(diào)度處理的。這里的輸入源可以是輸入設(shè)備、網(wǎng)絡(luò)、周期性或者延遲時(shí)間、異步回調(diào)
  • RunLoop 會(huì)接收兩種類(lèi)型的輸入源:一種是來(lái)自另一個(gè)線程或者來(lái)自不同應(yīng)用的異步消息;另一種是來(lái)自預(yù)訂時(shí)間或者重復(fù)間隔的同步事件
  • 當(dāng)有事件要去處理時(shí)保持線程忙,當(dāng)沒(méi)有事件要處理時(shí)讓線程進(jìn)入休眠
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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