原文:https://juejin.im/post/6862898534857834510
開篇
2020年注定是一個特殊且不平凡的一年崎场。疫情之下放仗,內(nèi)憂外患留夜,部分企業(yè)崎脉,倒下的倒下拧咳,扣薪的扣薪……,在這樣的大環(huán)境之下囚灼,即是危機也是機會骆膝,毅然決定踏上求職之路。起初自信滿滿灶体,在沒有做好充分準備情況之下阅签,簡歷寥寥草草,簡簡單單蝎抽,以至于錯失不少好機會政钟。切記切記!吃一塹長一智织中。最后通過優(yōu)化精簡排版簡歷锥涕,接到不少互聯(lián)網(wǎng)大廠的邀約面試。事實證明狭吼,擁有一份好簡歷,你已經(jīng)成功一半了殖妇。最終刁笙,通過兩個月的艱苦奮戰(zhàn),終于拿到自己比較滿意的offer。為了做個總結(jié)疲吸,特開此篇座每,僅供參考~
1、面試經(jīng)歷
坐標:深圳摘悴,面試公司數(shù):約15家峭梳, offer:到手的有2兩個,還有2家也進入談薪階段蹂喻,談完后就一直沒下文了葱椭,表示很郁悶。 面試方式:大部分采用遠程視頻面試口四,極少現(xiàn)場面試孵运。面試特點:一輪iOS技術(shù)面(OC基礎(chǔ)+OC底層+算法), 二輪廣泛技術(shù)面(網(wǎng)絡(luò)工程+數(shù)據(jù)結(jié)構(gòu)+算法)+HR面蔓彩≈伪浚總體感受,今年面試最大特點是赤嚼,首先.機會比往年少很多旷赖,iOS招聘需求主要集中在3-5年工作經(jīng)驗(換句話就是說崗位薪資20k普遍是上限,當然大廠除外)更卒,其次. 技術(shù)方面:OC底層已是必須掌握等孵,Swift極少被提到。
2逞壁、iOS高頻(基礎(chǔ)+底層)面試題
1. 你在開發(fā)過程中常用到哪些定時器流济,定時器時間會有誤差嗎,如果有腌闯,為什么會有誤差绳瘟?
iOS中常NSTimer、CADisplayLink、GCD定時器结借,其中NSTimer桐筏、CADisplayLink基于NSRunLoop實現(xiàn),故存在誤差蘸泻,GCD定時器只依賴系統(tǒng)內(nèi)核,相對一前兩者是比較準時的嘲玫。
誤差原因是:與NSRunLoop機制有關(guān)悦施, 因為RunLoop每跑完一次圈再去檢查當前累計時間是否已經(jīng)達到定時設(shè)置的間隔時間,如果未達到去团,RunLoop將進入下一輪任務(wù)抡诞,待任務(wù)結(jié)束之后再去檢查當前累計時間穷蛹,而此時的累計時間可能已經(jīng)超過了定時器的間隔時間,故會存在誤差昼汗。
參考《iOS常見三種定時器-NSTimer肴熏、CADisplayLink、GCD定時器》
作為一個開發(fā)者顷窒,有一個學習的氛圍跟一個交流圈子特別重要蛙吏,這是一個我的iOS交流群:761407670 不管你是小白還是大牛歡迎入駐 ,分享BAT,阿里面試題鞋吉、面試經(jīng)驗鸦做,討論技術(shù), 大家一起交流學習成長坯辩!
2. NSTimer馁龟、CADisplayLink會產(chǎn)生循環(huán)引用嗎?如果會漆魔,你是如何解決的坷檩?
如果直接使用,會產(chǎn)生循環(huán)引用問題改抡∈噶叮可以增加一個中間類,給這個類添加一個用weak修飾的id 類型target屬性阿纤,并重寫中間類的消息轉(zhuǎn)發(fā)方法句灌。實現(xiàn)如下代碼:
聲明文件.h:
#import <Foundation/Foundation.h>
@interface LXProxy : NSProxy
+ (instancetype)proxyWithTarget:(id)target;
@end
復(fù)制代碼
實現(xiàn)文件.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
復(fù)制代碼
調(diào)用代碼:
_timer = [NSTimer scheduledTimerWithTimeInterval:2 target:[LXProxy proxyWithTarget:self] selector:@selector(test) userInfo:nil repeats:YES];
復(fù)制代碼
3. 對Runtime有了解嗎,Runtime的方法查找過程是什么樣的欠拾?有哪些實際應(yīng)用胰锌?
runtime是OC動態(tài)語言的運行時機制,OC的方法調(diào)用最后都轉(zhuǎn)成了runtime的objc_msgSend函數(shù)藐窄。
3.1 Runtime消息傳遞:
<figcaption></figcaption>
- 通過哈希算法资昧,先從方法緩存中查找,如果命中荆忍,調(diào)用方法結(jié)束流程
- 如果緩存中沒有格带,則去當前類的方法列表中查找,如果命中刹枉,調(diào)用方法叽唱,加入當前方法緩存中,結(jié)束流程
- 如果當前類沒有對應(yīng)方法微宝,則去逐級父類方法列表中查找棺亭,如果命中,調(diào)用方法蟋软,加入當前方法緩存中侦铜,結(jié)束流程
4.如果方法都不存在专甩,進入方法動態(tài)解析钟鸵,轉(zhuǎn)入消息轉(zhuǎn)發(fā)流程钉稍。
注:對于已經(jīng)排序好的方法列表,采用二分查算法查找對應(yīng)的執(zhí)行函數(shù)棺耍,對應(yīng)沒有排序的列表贡未,采用一般遍歷方法查找對應(yīng)執(zhí)行函數(shù)。
3.2 消息轉(zhuǎn)發(fā)流程:
<figcaption></figcaption>
- 調(diào)用動態(tài)解析方法resolveClassMethod:(SEL)sel,如果動態(tài)添加方法(調(diào)用class_addMethod函數(shù))并返回YES,則結(jié)束流程
- 如果上一步?jīng)]有實現(xiàn)動態(tài)添加方法蒙袍,無論返回Yes還是No,都會調(diào)用消息接受者重定向forwardingTargetForSelector方法俊卤,如果返回重定向接受者,則當前流程結(jié)束
- 如果返回上一步nil,則會調(diào)用methodSignatureForSelector獲取函數(shù)的參數(shù)和返回值類型害幅,同時調(diào)用forwardInvocation消息通知當前對象消恍。
- 如果上一步返回nil,消息無法處理,App crash以现。
3.3 繼承關(guān)系:
<figcaption></figcaption>
- 實例對象(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),如果根類對象存在同名實例方法,則會調(diào)用同名實例方法
3.4 Runtime實際運用:
- 給NSTimer定時器聲明一個中間類Proxy(消息轉(zhuǎn)發(fā))
- 通過rumtime動態(tài)獲取類的所有屬性(json轉(zhuǎn)model纪吮、可歸檔類對屬性的歸檔及解歸檔操作)
- 反射機制(NSClassFromString, CTMediator原理)
- 交換系統(tǒng)方法(比如交換viewController生命周期方法俩檬,從而進行統(tǒng)一埋點等操作)
- 給分類添加屬性(通過關(guān)聯(lián)對象,實現(xiàn)getter, setter方法)
4. +load和+initlize調(diào)用時機碾盟?現(xiàn)在有一個類棚辽,給其添加了多個分類,并且每個實現(xiàn)分類都實現(xiàn)了相同的類方法(比如+test)巷疼,在調(diào)用這個方法時晚胡,會調(diào)用到哪個分類?
+initialize 方法,會在第一次初始化這個類之前被調(diào)用嚼沿,我們用它來初始化靜態(tài)變量估盘。+load 方法會在加載類的時候就被調(diào)用,也就是 ios 應(yīng)用啟動的時候骡尽,就會加載所有的類遣妥,就會調(diào)用每個類的 +load 方法。initialize 方法類似一個懶加載攀细,如果沒有使用這個類箫踩,那么系統(tǒng)默認不會去調(diào)用這個方法爱态,且默認只加載一次,且調(diào)用發(fā)生在 +init 方法之前。
調(diào)用最后參與編譯的分類的test方法境钟。原因:Xcode在編譯時根據(jù)buildPhases->Compile Sources里面的從上至下順序編譯的锦担,通過壓棧的方式將多個分類壓棧,且根據(jù)后進先出的原則慨削,后編譯的會被先調(diào)用(插入頂部添加洞渔,即[methodLists insertObject:category_method atIndex:0]。所以objc_msgSend遍歷方法列表查找SEL 對應(yīng)的IMP時缚态,會先找到最后參與編譯的分類)當objc_msgSend找到方法并調(diào)用之后磁椒,結(jié)束傳遞消息,所以就形成了所謂的“覆蓋”玫芦。
5. App冷啟動優(yōu)化浆熔?
App冷啟動優(yōu)化方案博客非常之多,概括總結(jié)大致如下:
- pre-main優(yōu)化:減少動態(tài)靜態(tài)庫桥帆,合并動態(tài)庫医增,移除廢棄第三方庫及所依賴的系統(tǒng)庫,二進制重排(抖音優(yōu)化方案)
- runtime對類的注冊环葵,類對象的初始化调窍,load方法加載階段:精簡類,合并分類张遭,移除廢棄分類等等
- main函數(shù)之后邓萨,推遲對三方庫注冊及延時調(diào)用耗時操作函數(shù)【站恚可以通過Instruments-->Time Profiler: 性能分析,定位耗時函數(shù)
6. UIView和CALayer有了解嗎缔恳,UI卡頓原因是什么,什么是離屏渲染洁闰,為什么會產(chǎn)生離屏渲染歉甚,如何避免觸發(fā)離屏渲染?
- UIView和CALayer遵循單一職責原則扑眉,UIView負責事件處理纸泄,參與響應(yīng)鏈,為CALayer提供顯示的內(nèi)容腰素,CALayer負責內(nèi)容顯示聘裁。
- UI卡頓原因:參考
7. 事件響應(yīng)響應(yīng)鏈是什么樣的?touchbegin弓千,button touch衡便,手勢的區(qū)別和聯(lián)系?
8. 實際開發(fā)過程當中,您使用到哪些設(shè)計模式镣陕?說說單例模式優(yōu)缺點谴餐?蘋果設(shè)計的類對象是不是單例模式?
9. 實際開發(fā)過程當中呆抑,您使用到哪些多線程岂嗓,GCD與NSOperationQueue有什么聯(lián)系?
10. Runloop響應(yīng)事件類型理肺,常用幾種mode類型摄闸,與GCD有什么聯(lián)系?
11. 說說你對Block的理解妹萨,有幾種類型的Block, Block在捕獲自變量,局部靜態(tài)變量炫欺,全局變量乎完,全局靜態(tài)變量有什么區(qū)別, 什么情況下要注意Block循環(huán)引用問題?
12. NSString屬性品洛,使用什么關(guān)鍵字修飾树姨,使用copy和strong修飾,有什么區(qū)別桥状?
13. 什么是引用計數(shù)帽揪,說說你對自動釋放池的理解,它是什么時候釋放的辅斟,為什么用__weak修飾的變量所指向的對象在釋放時會自動把變量指針置為nil转晰?
3、網(wǎng)絡(luò)工程面試題
1. HTTPS和HTTP有什么區(qū)別士飒,HTTPS加密過程是什么樣的查邢,對稱加密和非對稱解密各有什么優(yōu)缺點?
HTTPS協(xié)議是由SSL/TLS+HTTP協(xié)議構(gòu)建的可進行加密傳輸酵幕、身份認證的網(wǎng)絡(luò)協(xié)議扰藕,要比http協(xié)議安全
HTTPS協(xié)議的主要作用可以分為兩種:一種是建立一個信息安全通道,來保證數(shù)據(jù)傳輸?shù)陌踩既觯涣硪环N就是確認網(wǎng)站的真實性邓深。
HTTP與HTTPS的區(qū)別,詳細介紹
2. TCP和UDP有什么區(qū)別笔刹,TCP是可靠傳輸嗎芥备,如果保證其可靠性?
2.1 TCP和UDP區(qū)別:
- TCP面向連接(如打電話要先撥號建立連接);UDP是無連接的徘熔,即發(fā)送數(shù)據(jù)之前不需要建立連接
- TCP提供可靠的服務(wù)门躯。也就是說,通過TCP連接傳送的數(shù)據(jù)酷师,無差錯讶凉,不丟失染乌,不重復(fù),且按序到達;UDP盡最大努力交付懂讯,即不保證可靠交付
- TCP面向字節(jié)流荷憋,實際上是TCP把數(shù)據(jù)看成一連串無結(jié)構(gòu)的字節(jié)流;UDP是面向報文的UDP沒有擁塞控制,因此網(wǎng)絡(luò)出現(xiàn)擁塞不會使源主機的發(fā)送速率降低(對實時應(yīng)用很有用褐望,如IP電話勒庄,實時視頻會議等)
- 每一條TCP連接只能是點到點的;UDP支持一對一,一對多瘫里,多對一和多對多的交互通信
- TCP首部開銷20字節(jié);UDP的首部開銷小实蔽,只有8個字節(jié)
- TCP的邏輯通信信道是全雙工的可靠信道,UDP則是不可靠信道
參考1
2.2 TCP可靠性:
- 校驗和
- 確認應(yīng)答與序列號
- 超時重傳
- 連接管理
- 流量控制
- 擁塞控制(慢啟動谨读,擁塞避免局装,快重傳,快恢復(fù))
參考2
3. 如何針對App弱網(wǎng)情況優(yōu)化
4劳殖、算法編程面試題
1. 判斷一個單向鏈表是否有環(huán)铐尚?
1.快慢雙指針法,快指針一次走兩步哆姻,慢指針一次走一步宣增,如果有環(huán)必會相遇
public class ListNode {
public var val: Int
public var next: ListNode?
public init(_ val: Int) {
self.val = val
self.next = nil
}
}
func validedCycleNoded(_ node: ListNode?) -> Bool {
if node == nil {
return false
}
var fast = node, slow = node
while fast != nil {
fast = fast?.next?.next
slow = slow?.next
if fast?.val == slow?.val {
return true
}
}
return false
}
復(fù)制代碼
- 可以使用集合(Set)來判斷,來一次遍歷矛缨,把所有node添加到集合中爹脾,如果有重復(fù),則肯定要有環(huán)
2. 如何計算二叉樹的高度劳景?
遞歸算法
/**
* Definition for a binary tree node.
* public class TreeNode {
* public var val: Int
* public var left: TreeNode?
* public var right: TreeNode?
* public init(_ val: Int) {
* self.val = val
* self.left = nil
* self.right = nil
* }
* }
*/
class Solution {
func maxDepth(_ root: TreeNode?) -> Int {
guard let root = root else {return 0;}
return max(maxDepth(root.left), maxDepth(root.right)) + 1
}
}
復(fù)制代碼
3. 合并兩個有序數(shù)組誉简,同時去重
func mergeSortedArray(_ a: [Int], b:[Int]) ->[Int] {
var i = 0
var j = 0
var ans = [Int]()
//合并數(shù)組
while i < a.count && j < b.count {
if a[i] > b[j] {
ans.append(b[j])
j += 1
}else if (a[i] == b[j]) {
ans.append(b[j])
j += 1
i += 1
}else {
ans.append(a[i])
i += 1
}
}
//數(shù)組a有未合并元素
while i < a.count {
ans.append(a[i])
i += 1
}
//數(shù)組b有未合并元素
while j < b.count {
ans.append(b[j])
j += 1
}
return ans
}
復(fù)制代碼
4. 字符串編輯最短距離(LeeCode)
LeeCode-72.編輯距離
解法:動態(tài)規(guī)劃
5. 判斷括號的有效性(LeeCode)
class Solution {
func isValid(_ s: String) -> Bool {
if s.isEmpty {
return true;
}
let map = ["}":"{", ")":"(","]":"["]
var stack = [String]()
for c in s {
let key = String(c)
if key == "{" || key == "(" || key == "[" {
stack.append(key)
} else if !stack.isEmpty && map[key] == stack.last {
stack.removeLast()
} else {
return false
}
}
return stack.isEmpty
}
}
復(fù)制代碼
6. 25匹馬,現(xiàn)有5條跑道盟广,沒有計時器闷串,要找出最快3匹馬,至少要跑幾場筋量?
至少跑7場烹吵,
- 對25匹馬隨機分成5個組(A,B,C,D,E,F),每組跑一場,記錄每一匹馬在當前組中名次(第1名桨武,第2名肋拔,第3名)(跑了五場)
- 從各個組中選取名次為第一名的馬組成一組,跑一場呀酸,記錄名次(第六場)凉蜂,本組第1名則確定了25匹馬中最快的一匹馬
- 選取第六場中名次為第1名的所在原來組名次為第2、3名馬,選取第六場中名次為第2名的所在原來組名次第1窿吩、2名馬(它自己+第2名)茎杂,選取第六場中名次為第3名所在原來組名次第1名的馬(它自己),組成一組纫雁,跑一場煌往,記錄名次(第七場),本場的第1轧邪、2名就是25匹馬中最快的第2刽脖、3名
7. 8瓶液體,其中1瓶有毒藥忌愚,毒藥1小時后至死曲管,請問最快找出毒藥,需要幾只老鼠菜循?
1只老鼠可以斷定2瓶液體翘地,2^3=8,所以需要3只老鼠即可癌幕,
對液體進行編號,001昧穿,010勺远,011,100时鸵,101胶逢,110,111
給1號老鼠喂編碼個位數(shù)上是1的液體(001饰潜,011初坠,101,111)彭雾,
給2號老鼠喂編碼十位數(shù)上是1的液體(010碟刺,011,110薯酝,111)半沽,
給3號老鼠喂編碼百位數(shù)上是1的液體(100,101吴菠,110者填,111),
1小時后做葵,
如果老鼠全活, 8號液體有毒占哟,
如果全都死,7號液體有毒,
如果1號死榨乎,2怎燥,3活, 1號液體有毒
如果2號死谬哀, 1刺覆,3活,2號液體有毒
如果3號死史煎,1谦屑,2活, 4號液體有毒
如果1篇梭,2號死氢橙,3活, 3號液體有毒
如果1恬偷,3號死悍手,2活, 5號液體有毒
如果2袍患,3號死坦康,1活, 6號液體有毒
5诡延、其他面試題
1. 如下結(jié)構(gòu)體滞欠,大小是多少?
struct Node {
char a;
int b;
} node;
復(fù)制代碼
結(jié)構(gòu)體大小是8肆良,考察結(jié)構(gòu)體特性筛璧,內(nèi)存對齊原則。
2. 定義一個全局變量a = 0; 開辟兩條子線程訪問 a = a + 1; 各for loop 10次惹恃,a的最終結(jié)果是多少夭谤?
<=20,線程安全問題。
3. 公司員工表(user)中有入職時間(t1)和離職時間(t2)巫糙,請編寫sql語句朗儒,查詢18年3月(date1)-18年6月(date2)所有在職員工人信息
select * from user where 入職時間<201806 and (離職時間 is null or離職時間>201803)。
由于時間關(guān)系曲秉,后面慢慢完善面試題答案采蚀。最后借用《三十而已》電視劇的臺詞作為結(jié)尾:“以上就是我面試遇到的故事,未完待續(xù)~~~”
作為一個開發(fā)者承二,有一個學習的氛圍跟一個交流圈子特別重要榆鼠,這是一個我的iOS交流群:761407670 不管你是小白還是大牛歡迎入駐 ,分享BAT,阿里面試題亥鸠、面試經(jīng)驗妆够,討論技術(shù)识啦, 大家一起交流學習成長!