1. 你在開發過程中常用到哪些定時器,定時器時間會有誤差嗎,如果有,爲什麼會有誤差?
iOS中常NSTimer、CADisplayLink、GCD定時器,其中NSTimer、CADisplayLink基於NSRunLoop實現,故存在誤差,GCD定時器只依賴系統內核,相對一前兩者是比較準時的。
誤差原因是:與NSRunLoop機制有關, 因爲RunLoop每跑完一次圈再去檢查當前累計時間是否已經達到定時設置的間隔時間,如果未達到,RunLoop將進入下一輪任務,待任務結束之後再去檢查當前累計時間,而此時的累計時間可能已經超過了定時器的間隔時間,故會存在誤差。
參考《iOS常見三種定時器-NSTimer、CADisplayLink、GCD定時器》
2. NSTimer、CADisplayLink會產生循環引用嗎?如果會,你是如何解決的?
如果直接使用,會產生循環引用問題。可以增加一箇中間類,給這個類添加一個用weak修飾的id 類型target屬性,並重寫中間類的消息轉發方法。實現如下代碼:
聲明文件.h:
#import <Foundation/Foundation.h> @interface LXProxy : NSProxy + (instancetype)proxyWithTarget:(id)target; @end
實現文件.m
#import "LXProxy.h" @interface LXProxy () /** weak target*/ @property (nonatomic, weak) id target; @end @implementation LXProxy + (instancetype)proxyWithTarget:(id)target{ LXProxy *proxy = [LXProxy alloc]; proxy.target = target; return proxy; } - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{ return [self.target methodSignatureForSelector:sel]; } - (void)forwardInvocation:(NSInvocation *)invocation{ [invocation invokeWithTarget:self.target]; } @end
調用代碼:
_timer = [NSTimer scheduledTimerWithTimeInterval:2 target:[LXProxy proxyWithTarget:self] selector:@selector(test) userInfo:nil repeats:YES];
3. 對Runtime有了解嗎,Runtime的方法查找過程是什麼樣的?有哪些實際應用?
runtime是OC動態語言的運行時機制,OC的方法調用最後都轉成了runtime的objc_msgSend函數。
3.1 Runtime消息傳遞:
- 通過哈希算法,先從方法緩存中查找,如果命中,調用方法結束流程
- 如果緩存中沒有,則去當前類的方法列表中查找,如果命中,調用方法,加入當前方法緩存中,結束流程
- 如果當前類沒有對應方法,則去逐級父類方法列表中查找,如果命中,調用方法,加入當前方法緩存中,結束流程
- 如果方法都不存在,進入方法動態解析,轉入消息轉發流程。
注:對於已經排序好的方法列表,採用二分查算法查找對應的執行函數,對應沒有排序的列表,採用一般遍歷方法查找對應執行函數。
3.2 消息轉發流程:
- 調用動態解析方法resolveClassMethod:(SEL)sel,如果動態添加方法(調用class_addMethod函數)並返回YES,則結束流程
- 如果上一步沒有實現動態添加方法,無論返回Yes還是No,都會調用消息接受者重定向forwardingTargetForSelector方法,如果返回重定向接受者,則當前流程結束
- 如果返回上一步nil,則會調用methodSignatureForSelector獲取函數的參數和返回值類型,同時調用forwardInvocation消息通知當前對象。
- 如果上一步返回nil,消息無法處理,App crash。
3.3 繼承關係:
- 實例對象(isntance)的isa指針指向類對象(class),類對象的存放實例方法(-方法)
- 類對象(class)的isa指針指向其元類對象(meta), 元類對象存放類方法(+方法)
- 根類對象(root class)的isa指針指向根元類對象(root meta),superclass指針指向nil.
- 根元類對象(root meta)的isa指針指向自己,superclass指針根類對象(root class)
由此可知, 實例方法(-方法)查找是沿着其superclass指針逐級父類查找,終於根類對象(root class)。而類方法(+方法)查找是沿着其superclass指針逐級父類(meta)查找,終於根類對象(root class),如果根類對象存在同名實例方法,則會調用同名實例方法
3.4 Runtime實際運用:
- 給NSTimer定時器聲明一箇中間類Proxy(消息轉發)
- 通過rumtime動態獲取類的所有屬性(json轉model、可歸檔類對屬性的歸檔及解歸檔操作)
- 反射機制(NSClassFromString, CTMediator原理)
- 交換系統方法(比如交換viewController生命週期方法,從而進行統一埋點等操作)
- 給分類添加屬性(通過關聯對象,實現getter, setter方法)
4. +load和+initlize調用時機?現在有一個類,給其添加了多個分類,並且每個實現分類都實現了相同的類方法(比如+test),在調用這個方法時,會調用到哪個分類?
-
+initialize 方法,會在第一次初始化這個類之前被調用,我們用它來初始化靜態變量。+load 方法會在加載類的時候就被調用,也就是 ios 應用啓動的時候,就會加載所有的類,就會調用每個類的 +load 方法。initialize 方法類似一個懶加載,如果沒有使用這個類,那麼系統默認不會去調用這個方法,且默認只加載一次,且調用發生在 +init 方法之前。
-
調用最後參與編譯的分類的test方法。原因:Xcode在編譯時根據buildPhases->Compile Sources裏面的從上至下順序編譯的,通過壓棧的方式將多個分類壓棧,且根據後進先出的原則,後編譯的會被先調用(插入頂部添加,即[methodLists insertObject:category_method atIndex:0]。所以objc_msgSend遍歷方法列表查找SEL 對應的IMP時,會先找到最後參與編譯的分類)當objc_msgSend找到方法並調用之後,結束傳遞消息,所以就形成了所謂的“覆蓋”。
5. App冷啓動優化?
App冷啓動優化方案博客非常之多,概括總結大致如下:
- pre-main優化:減少動態靜態庫,合併動態庫,移除廢棄第三方庫及所依賴的系統庫,二進制重排(抖音優化方案)
- runtime對類的註冊,類對象的初始化,load方法加載階段:精簡類,合併分類,移除廢棄分類等等
- main函數之後,推遲對三方庫註冊及延時調用耗時操作函數。可以通過Instruments-->Time Profiler: 性能分析,定位耗時函數
6. UIView和CALayer有了解嗎,UI卡頓原因是什麼,什麼是離屏渲染,爲什麼會產生離屏渲染,如何避免觸發離屏渲染?
- UIView和CALayer遵循單一職責原則,UIView負責事件處理,參與響應鏈,爲CALayer提供顯示的內容,CALayer負責內容顯示。
- UI卡頓原因:參考
3、網絡工程面試題
1. HTTPS和HTTP有什麼區別,HTTPS加密過程是什麼樣的,對稱加密和非對稱解密各有什麼優缺點?
HTTPS協議是由SSL/TLS+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,要比http協議安全
HTTPS協議的主要作用可以分爲兩種:一種是建立一個信息安全通道,來保證數據傳輸的安全;另一種就是確認網站的真實性。
2. TCP和UDP有什麼區別,TCP是可靠傳輸嗎,如果保證其可靠性?
2.1 TCP和UDP區別:
- TCP面向連接(如打電話要先撥號建立連接);UDP是無連接的,即發送數據之前不需要建立連接
- TCP提供可靠的服務。也就是說,通過TCP連接傳送的數據,無差錯,不丟失,不重複,且按序到達;UDP盡最大努力交付,即不保證可靠交付
- TCP面向字節流,實際上是TCP把數據看成一連串無結構的字節流;UDP是面向報文的UDP沒有擁塞控制,因此網絡出現擁塞不會使源主機的發送速率降低(對實時應用很有用,如IP電話,實時視頻會議等)
- 每一條TCP連接只能是點到點的;UDP支持一對一,一對多,多對一和多對多的交互通信
- TCP首部開銷20字節;UDP的首部開銷小,只有8個字節
- TCP的邏輯通信信道是全雙工的可靠信道,UDP則是不可靠信道
參考1
2.2 TCP可靠性:
- 校驗和
- 確認應答與序列號
- 超時重傳
- 連接管理
- 流量控制
- 擁塞控制(慢啓動,擁塞避免,快重傳,快恢復)
參考2
3. 如何針對App弱網情況優化