14. RunLoop的基礎(chǔ)知識
- RunLoop模式有哪些?
答 : iOS中有五種RunLoop模式
NSDefaultRunLoopMode (默認(rèn)模式岖赋,有事件響應(yīng)的時候炒瘟,會阻塞舊事件)
NSRunLoopCommonModes (普通模式,不會影響任何事件)
UITrackingRunLoopMode (只能是有事件的時候才會響應(yīng)的模式)
還有兩種系統(tǒng)級別的模式
一個是app剛啟動的時候會執(zhí)行一次
另外一個是系統(tǒng)檢測app各種事件的模式
- RunLoop的基本執(zhí)行原理
答 : 原本系統(tǒng)就有一個runloop在檢測App內(nèi)部的行為或事件破加,當(dāng)輸入源(用戶的直接或者間接的操作)有“執(zhí)行操作”的時候梁钾, 系統(tǒng)的runloop會監(jiān)聽輸入源的狀態(tài)武契, 進(jìn)而在系統(tǒng)內(nèi)部做一些對應(yīng)的相應(yīng)操作。 處理完成后发笔,會自動回到睡眠狀態(tài)盟萨, 等待下一次被喚醒,
RunLoop和線程的關(guān)系
RunLoop的作用就是用來管理線程的了讨, 當(dāng)線程的RunLoop開啟之后捻激,線程就會在執(zhí)行完成任務(wù)后,進(jìn)入休眠狀態(tài)前计,隨時等待接收新的任務(wù)胞谭,而不是退出。
為什么只有主線程的
runloop
是開啟的程序開啟之后男杈,要一直運(yùn)行丈屹,不會退出。 說白了就是為了讓程序不死
如何保證一個線程永遠(yuǎn)不死(常駐線程)
// 先創(chuàng)建一個線程用于測試
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(play) object:nil];
[thread start];
// 保證一個線程永遠(yuǎn)不死
[[NSRunLoop currentRunLoop] addPort:[NSPort port] -forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
// 在合適的地方處理線程的事件處理
[self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:NO];
15. weak屬性伶棒?
1. 說說你理解weak屬性?
1.實現(xiàn)weak后旺垒,為什么對象釋放后會自動為nil?
runtime 對注冊的類肤无, 會進(jìn)行布局先蒋,
對于 weak 對象會放入一個 hash 表中。
用 weak 指向的對象內(nèi)存地址作為 key宛渐,
Value是weak指針的地址數(shù)組竞漾。
當(dāng)釋放的時候眯搭,其內(nèi)部會通過當(dāng)前的key找到所有的weak指針指向的數(shù)組
然后遍歷這個數(shù)組把其中的數(shù)據(jù)設(shè)置為nil。
稍微詳細(xì)的說:在內(nèi)部底層源碼也同時和當(dāng)前對象相關(guān)聯(lián)得SideTable业岁, 其內(nèi)部有三個屬性鳞仙, 一個是一把自旋鎖,一個是引用計數(shù)器相關(guān)笔时,一個是維護(hù)weak生命得屬性得表
**SideTable**這個結(jié)構(gòu)體一樣的東西棍好,可以花半個小時看一眼。
延伸
objc中向一個nil對象發(fā)送消息將會發(fā)生什么糊闽?
首先 在尋找對象化的isa指針時就是0地址返回了梳玫, 所以不會有任何錯誤, 也不會錯誤objc在向一個對象發(fā)送消息時右犹,發(fā)生了什么?
- 首先是通過obj 的isa指針找到對應(yīng)的class
- 先去操作對象中的緩存方法列表中objc_cache中去尋找 當(dāng)前方法姚垃,如果找到就直接實現(xiàn)對應(yīng)IMP
- 如果在緩存中找不到,則在class中找到對用的Method list中對用foo
- 如果class中沒有找到對應(yīng)的foo, 就會去superClass中去找
- 如果找到了對應(yīng)的foo奠伪, 就會實現(xiàn)foo對應(yīng)的IMP
緩存方法列表铝宵, 就是每次執(zhí)行這個方法的時候都會做如此繁瑣的操作這樣太過于消耗性能,所以出現(xiàn)了一個objc_cache看成,這個會把當(dāng)前調(diào)用過的類中的方法做一個緩存君编, 當(dāng)前method_name作為key, method_IMP作為Value川慌,當(dāng)再一次接收到消息的時候吃嘿,直接通過objc_cache去找到對應(yīng)的foo的IMP即可, 避免每一次都去遍歷objc_method_list
如果一直沒有找到方法梦重, 就會專用消息轉(zhuǎn)發(fā)機(jī)制兑燥,機(jī)制如下
// 動態(tài)方法解析和轉(zhuǎn)發(fā)
上面的例子如果foo函數(shù)一直沒有被找到,通常情況下琴拧,會出現(xiàn)報錯降瞳,但是在報錯之前,OC的運(yùn)行時給了我們?nèi)窝a(bǔ)救的機(jī)會
- Method resolution
- Fast forwarding
- Normal forwarding
1. Runtime 會發(fā)送 +resolveInstanceMethod: 或者 +resolveClassMethod: 嘗試去 resolve(重啟) 這個消息蚓胸;
2. 如果 resolve 方法返回 NO挣饥,Runtime 就發(fā)送 -forwardingTargetForSelector: 允許你把這個消息轉(zhuǎn)發(fā)給另一個對象;
3. 如果沒有新的目標(biāo)對象返回沛膳, Runtime 就會發(fā)送 -methodSignatureForSelector: 和 -forwardInvocation: 消息扔枫。你可以發(fā)送 -invokeWithTarget: 消息來手動轉(zhuǎn)發(fā)消息或者發(fā)送 -doesNotRecognizeSelector: 拋出異常。
消息轉(zhuǎn)發(fā)詳情如下圖:
[圖片上傳失敗...(image-3df667-1596096855494)]
16.UIView和CALayer是什么關(guān)系?
- 兩者最明顯的區(qū)別是 View可以接受并處理事件于置,而 Layer 不可以茧吊;
- 每個 UIView 內(nèi)部都有一個 CALayer 在背后提供內(nèi)容的繪制和顯示贞岭,并且 UIView 的尺寸樣式都由內(nèi)部的 Layer 所提供。兩者都有樹狀層級結(jié)構(gòu)搓侄,layer 內(nèi)部有 SubLayers瞄桨,View 內(nèi)部有 SubViews.但是 Layer 比 View 多了個AnchorPoint
- 在 View顯示的時候,UIView 做為 Layer 的 CALayerDelegate,View 的顯示內(nèi)容由內(nèi)部的 CALayer 的 display
CALayer 是默認(rèn)修改屬性支持隱式動畫的讶踪,在給 UIView 的 Layer 做動畫的時候芯侥,View 作為 Layer 的代理,Layer 通過 actionForLayer:forKey:向 View請求相應(yīng)的 action(動畫行為)
- layer 內(nèi)部維護(hù)著三分 layer tree,分別是 presentLayer Tree(動畫樹),modeLayer Tree(模型樹), Render Tree (渲染樹),在做 iOS動畫的時候乳讥,我們修改動畫的屬性柱查,在動畫的其實是 Layer 的 presentLayer的屬性值,而最終展示在界面上的其實是提供 View的modelLayer
16. @synthesize 和 @dynamic 分別有什么作用
- @property有兩個對應(yīng)的詞,一個是 @synthesize云石,一個是 @dynamic唉工。如果 @synthesize和 @dynamic都沒寫,那么默認(rèn)的就是@syntheszie var = _var;
- @synthesize 的語義是如果你沒有手動實現(xiàn) setter 方法和 getter 方法汹忠,那么編譯器會自動為你加上這兩個方法淋硝。
- @dynamic 告訴編譯器:屬性的 setter 與 getter 方法由用戶自己實現(xiàn),不自動生成宽菜。(當(dāng)然對于 readonly 的屬性只需提供 getter 即可)谣膳。假如一個屬性被聲明為 @dynamic var,然后你沒有提供 @setter方法和 @getter 方法铅乡,編譯的時候沒問題继谚,但是當(dāng)程序運(yùn)行到 instance.var = someVar,由于缺 setter 方法會導(dǎo)致程序崩潰阵幸;或者當(dāng)運(yùn)行到 someVar = var 時花履,由于缺 getter 方法同樣會導(dǎo)致崩潰。編譯時沒問題侨嘀,運(yùn)行時才執(zhí)行相應(yīng)的方法臭挽,這就是所謂的動態(tài)綁定。
17. static有什么作用?
static關(guān)鍵字可以修飾函數(shù)和變量咬腕,作用如下:
**隱藏**
通過static修飾的函數(shù)或者變量欢峰,在該文件中,所有位于這條語句之后的函數(shù)都可以訪問涨共,而其他文件中的方法和函數(shù)則不行
**靜態(tài)變量**
類方法不可以訪問實例變量(函數(shù))纽帖,通過static修飾的實例變量(函數(shù)),可以被類 方法訪問举反;
**持久**
static修飾的變量懊直,能且只能被初始化一次;
**默認(rèn)初始化**
static修飾的變量火鼻,默認(rèn)初始化為0室囊;
18. objc在向一個對象發(fā)送消息時雕崩,發(fā)生了什么?
- objc_msgSend(recicver, selecter..)
19. runloop是來做什么的融撞?runloop和線程有什么關(guān)系盼铁?主線程默認(rèn)開啟了runloop么?子線程呢尝偎?
1. runloop與線程是一一對應(yīng)的饶火,一個runloop對應(yīng)一個核心的線程,為什么說是核心的致扯,是因為runloop是可以嵌套的肤寝,但是核心的只能有一個,他們的關(guān)系保存在一個全局的字典里抖僵。
2. runloop是來管理線程的鲤看,當(dāng)線程的runloop被開啟后,線程會在執(zhí)行完任務(wù)后進(jìn)入休眠狀態(tài)耍群,有了任務(wù)就會被喚醒去執(zhí)行任務(wù)刨摩。runloop在第一次獲取時被創(chuàng)建,在線程結(jié)束時被銷毀世吨。
3. 對于主線程來說,runloop在程序一啟動就默認(rèn)創(chuàng)建好了呻征。
4. 對于子線程來說耘婚, runloop是懶加載的,只有當(dāng)我們使用的時候才會創(chuàng)建陆赋,所以在子線程用定時器要注意:確保子線程的runloop被開啟沐祷,不然定時器不會回調(diào)。
20. 如何手動觸發(fā)一個value的KVO
鍵值觀察通知依賴于 NSObject 的兩個方法: willChangeValueForKey: 和 didChangevlueForKey: 攒岛。在一個被觀察屬性發(fā)生改變之前赖临, willChangeValueForKey: 一定會被調(diào)用,這就
會記錄舊的值灾锯。而當(dāng)改變發(fā)生后兢榨, didChangeValueForKey: 會被調(diào)用,繼而 observeValueForKey:ofObject:change:context: 也會被調(diào)用顺饮。如果可以手動實現(xiàn)這些調(diào)用吵聪,就可以實現(xiàn)“手動觸發(fā)”了。
引申 0 如何給系統(tǒng)KVO設(shè)置篩選條件兼雄?
- 舉例:取消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;
}
}
引申 1.通過KVC修改屬性會觸發(fā)KVO么赦肋?直接修改成員變量呢 块攒?
- 會觸發(fā)KVO励稳。即使沒有聲明屬性,只有成員變量囱井,只要accessInstanceVariablesDirectly返回的是YES驹尼,允許訪問其成員變量,那么不管有沒有調(diào)用setter方法琅绅,通過KVC修改成員變量的值扶欣,都能觸發(fā)KVO。這也說明通過KVC內(nèi)部實現(xiàn)了willChangeValueForKey:方法和didChangeValueForKey:方法
- 直接修改成員變量不會觸發(fā)KVO千扶。直接修改成員變量內(nèi)部并沒有做處理只是單純的賦值料祠,所以不會觸發(fā)。
引申 kvc的底層實現(xiàn)澎羞?
- 賦值方法setValue:forKey:的原理
(1)首先會按照順序依次查找setKey:方法和_setKey:方法髓绽,只要找到這兩個方法當(dāng)中的任何一個就直接傳遞參數(shù),調(diào)用方法妆绞;
(2)如果沒有找到setKey:和_setKey:方法顺呕,那么這個時候會查看accessInstanceVariablesDirectly方法的返回值,如果返回的是NO(也就是不允許直接訪問成員變量)括饶,那么會調(diào)用setValue:forUndefineKey:方法株茶,并拋出異常“NSUnknownKeyException”图焰;
(3)如果accessInstanceVariablesDirectly方法返回的是YES启盛,也就是說可以訪問其成員變量,那么就會按照順序依次查找 _key技羔、_isKey僵闯、key、isKey這四個成員變量藤滥,如果查找到了鳖粟,就直接賦值;如果依然沒有查到拙绊,那么會調(diào)用setValue:forUndefineKey:方法向图,并拋出異常“NSUnknownKeyException”时呀。
- 取值方法valueForKey:的原理
(1)首先會按照順序依次查找getKey:张漂、key、isKey谨娜、_key:這四個方法航攒,只要找到這四個方法當(dāng)中的任何一個就直接調(diào)用該方法;
(2)如果沒有找到趴梢,那么這個時候會查看accessInstanceVariablesDirectly方法的返回值漠畜,如果返回的是NO(也就是不允許直接訪問成員變量)币他,那么會調(diào)用valueforUndefineKey:方法,并拋出異炽灸“NSUnknownKeyException”蝴悉;
(3)如果accessInstanceVariablesDirectly方法返回的是YES,也就是說可以訪問其成員變量瘾敢,那么就會按照順序依次查找 _key拍冠、_isKey、key簇抵、isKey這四個成員變量庆杜,如果找到了,就直接取值碟摆;如果依然沒有找到成員變量晃财,那么會調(diào)用valueforUndefineKey方法,并拋出異车渫桑“NSUnknownKeyException”断盛。
21. ViewController生命周期
按照執(zhí)行順序排列:
1. initWithCoder:通過nib文件初始化時觸發(fā)。
2. awakeFromNib:nib文件被加載的時候愉舔,會發(fā)生一個awakeFromNib的消息到nib文件中的每個對象钢猛。
3. loadView:開始加載視圖控制器自帶的view。
4. viewDidLoad:視圖控制器的view被加載完成轩缤。
5. viewWillAppear:視圖控制器的view將要顯示在window上厢洞。
6. updateViewConstraints:視圖控制器的view開始更新AutoLayout約束。
7. viewWillLayoutSubviews:視圖控制器的view將要更新內(nèi)容視圖的位置典奉。
8. viewDidLayoutSubviews:視圖控制器的view已經(jīng)更新視圖的位置。
9. viewDidAppear:視圖控制器的view已經(jīng)展示到window上丧叽。
10. viewWillDisappear:視圖控制器的view將要從window上消失卫玖。
11. viewDidDisappear:視圖控制器的view已經(jīng)從window上消失。
22.網(wǎng)絡(luò)協(xié)議
- TCP三次握手和四次揮手踊淳?
三次握手
1.客戶端向服務(wù)端發(fā)起請求鏈接假瞬,首先發(fā)送SYN報文,SYN=1迂尝,seq=x,并且客戶端進(jìn)入SYN_SENT狀態(tài)
2.服務(wù)端收到請求鏈接脱茉,服務(wù)端向客戶端進(jìn)行回復(fù),并發(fā)送響應(yīng)報文垄开,SYN=1琴许,seq=y,ACK=1,ack=x+1,并且服務(wù)端進(jìn)入到SYN_RCVD狀態(tài)
3.客戶端收到確認(rèn)報文后,向服務(wù)端發(fā)送確認(rèn)報文溉躲,ACK=1榜田,ack=y+1益兄,此時客戶端進(jìn)入到ESTABLISHED,服務(wù)端收到用戶端發(fā)送過來的確認(rèn)報文后箭券,也進(jìn)入到ESTABLISHED狀態(tài)净捅,此時鏈接創(chuàng)建成功
- 哎!
- 嗯
- 給你
為什么需要三次握手:
為了防止已失效的連接請求報文段突然又傳送到了服務(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卻以為新的運(yùn)輸連接已經(jīng)建立拟逮,并一直等待client發(fā)來數(shù)據(jù)。這樣适滓,server的很多資源就白白浪費(fèi)掉了敦迄。
四次揮手
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ù)
- 哎!
- 嗯
- 關(guān)了
- 好的
為什么需要四次揮手:
因為TCP是全雙工通信的蚀狰,在接收到客戶端的關(guān)閉請求時愉昆,還可能在向客戶端發(fā)送著數(shù)據(jù),因此不能再回應(yīng)關(guān)閉鏈接的請求時麻蹋,同時發(fā)送關(guān)閉鏈接的請求
引申
-
HTTP和HTTPS有什么區(qū)別跛溉?
- HTTP協(xié)議是一種使用明文數(shù)據(jù)傳輸?shù)木W(wǎng)絡(luò)協(xié)議。
- HTTPS協(xié)議可以理解為HTTP協(xié)議的升級,就是在HTTP的基礎(chǔ)上增加了數(shù)據(jù)加密倒谷。在數(shù)據(jù)進(jìn)行傳輸之前蛛蒙,對數(shù)據(jù)進(jìn)行加密,然后再發(fā)送到服務(wù)器渤愁。這樣牵祟,就算數(shù)據(jù)被第三者所截獲,但是由于數(shù)據(jù)是加密的抖格,所以你的個人信息讓然是安全的诺苹。這就是HTTP和HTTPS的最大區(qū)別。
-
HTTPS的加密方式雹拄?
Https采用對稱加密和非對稱加密結(jié)合的方式來進(jìn)行通信收奔。
-
Https不是應(yīng)用層的新協(xié)議,而是Http通信接口用SSL和TLS來加強(qiáng)加密和認(rèn)證機(jī)制滓玖。
- 對稱加密: 加密和解密都是同一個鑰匙
- 非對稱加密:密鑰承兌出現(xiàn)坪哄,分為公鑰和私鑰,公鑰加密需要私鑰解密势篡,私鑰加密需要公鑰解密
HTTP和HTTPS的建立連接的過程翩肌?
HTTP
- 建立鏈接完畢以后客戶端會發(fā)送響應(yīng)給服務(wù)器
- 服務(wù)端接受請求并且做出響應(yīng)發(fā)送給客戶端
- 客戶端收到響應(yīng)并且解析響應(yīng)給客戶
HTTPS
- 在使用HTTPS是需要保證服務(wù)端配置了正確的對應(yīng)的安全證書
- 客戶端發(fā)送請求到服務(wù)器
- 服務(wù)端返回公鑰和證書到客戶端
- 客戶端接受后,會驗證證書的安全性禁悠,如果通過則會隨機(jī)生成一個隨機(jī)數(shù)念祭,用公鑰對其解密, 發(fā)送到服務(wù)端
- 服務(wù)端接受到這個加密后的隨機(jī)數(shù)后碍侦,會用私鑰對其進(jìn)行揭秘粱坤,得到真正的隨機(jī)數(shù),然后調(diào)用這個隨機(jī)數(shù)當(dāng)作私鑰對需要發(fā)送的數(shù)據(jù)進(jìn)行對稱加密瓷产。
- 客戶端接收到加密后的數(shù)據(jù)使用私鑰(之前生成的隨機(jī)值)對數(shù)據(jù)進(jìn)行解密站玄,并且解析數(shù)據(jù)呈現(xiàn)給客戶
HTTP協(xié)議中GET和POST的區(qū)別
GET在特定的瀏覽器和服務(wù)器對URL的長度是有限制的。 但是理論上是沒有限制的
POST不是通過URL進(jìn)行傳值濒旦,理論上不受限制蜒什。
GET會把請求參數(shù)拼接到URL后面, 不安全疤估,
POST把參數(shù)放到請求體里面, 會比GET相對安全一點霎冯, 但是由于可以窺探數(shù)據(jù)铃拇, 所以也不安全, 想更安全用加密沈撞。
GET比POST的請求速度快慷荔。原因:Post請求的過程, 會現(xiàn)將請求頭發(fā)送給服務(wù)器確認(rèn)缠俺,然后才真正的發(fā)送數(shù)據(jù)显晶, 而Get請求 過程會在鏈接建立后會將請求頭和數(shù)據(jù)一起發(fā)送給服務(wù)器贷岸。 中間少了一步。 所以get比post 快
post的請求過程
三次握手之后 第三次會把post請求頭發(fā)送
服務(wù)器返回100 continue響應(yīng)
瀏覽器開始發(fā)送數(shù)據(jù)
服務(wù)器返回200 ok響應(yīng)
- get請求過程
- 三次握手之后 第三次會發(fā)送get請求頭和數(shù)據(jù)
- 服務(wù)器返回200 ok響應(yīng)