iOS面試題
[toc]
一精算、設(shè)計(jì)基本原則
簡(jiǎn)述六大設(shè)計(jì)基本原則(也稱(chēng) SOLID 五大原則)
單一職責(zé)原則 (SRP, Single Responsibility Principle)
- 定義: 一個(gè)類(lèi)只負(fù)責(zé)一件事。
- 優(yōu)點(diǎn): 類(lèi)的復(fù)雜度降低、可讀性增強(qiáng)、易維護(hù)甚亭、變更引起的風(fēng)險(xiǎn)降低。
- 應(yīng)用: 系統(tǒng)提供的UIView和CALayer的關(guān)系:UIView負(fù)責(zé)時(shí)間傳遞、事件響應(yīng)婆瓜;CALayer負(fù)責(zé)動(dòng)畫(huà)及展示
開(kāi)閉原則(OCP, Open-Close Principle)
-
定義: 對(duì)修改關(guān)閉、對(duì)擴(kuò)展開(kāi)放.
- 設(shè)計(jì)的類(lèi)做好后就不再修改贡羔,如果有新的需求廉白,通過(guò)新加類(lèi)的方式來(lái)滿足个初,而不去修改現(xiàn)有的類(lèi)的代碼.
優(yōu)點(diǎn): 靈活、穩(wěn)定(不需修改內(nèi)部代碼蒙秒,使得被破壞的程度大大下降)
關(guān)鍵: 抽象化
-
使用:
- 我們可以把把行為添加到一個(gè)協(xié)議中勃黍,使用時(shí)遵守這個(gè)協(xié)議即可。
- 添加類(lèi)目(Category)方式創(chuàng)建
里氏替換原則 (LSP,Liskov Substitution Principle)
-
定義: 所有引用父類(lèi)的地方必須能透明地使用其子類(lèi)的對(duì)象晕讲。
- 通俗點(diǎn)說(shuō)就是,父類(lèi)可以被子類(lèi)無(wú)縫替換马澈,且原有功能不受任何影響
-
優(yōu)點(diǎn):
- 代碼共享瓢省,減少創(chuàng)建類(lèi)的工作量,每個(gè)子類(lèi)都擁有父類(lèi)的所有屬性和方法
- 提高代碼的可重用性痊班、擴(kuò)張性勤婚,項(xiàng)目的開(kāi)放性
缺點(diǎn): 程序的可移植性降低,增加了對(duì)象間的耦合性
接口隔離原則(ISP, Interface Segregation Principle)
-
定義: 客戶端不應(yīng)該依賴它不需要的接口
- 使用多個(gè)專(zhuān)門(mén)的協(xié)議涤伐、而不是一個(gè)龐大臃腫的協(xié)議馒胆。
- 協(xié)議中的方法應(yīng)當(dāng)盡量少
例: UITableViewDataSource、UITableViewDelegate
優(yōu)點(diǎn): 解耦凝果、增強(qiáng)可讀性祝迂、可擴(kuò)展性、可維護(hù)性
依賴倒置原則(DIP, Dependence Inversion Principle)
定義: 抽象不應(yīng)該依賴于具體實(shí)現(xiàn)器净,具體實(shí)現(xiàn)可以依賴于抽象
核心思想: 面向接口編程
優(yōu)點(diǎn): 代碼結(jié)構(gòu)清晰型雳,維護(hù)容易
實(shí)例: 平時(shí)我們使用 protocol 匿名對(duì)象模式就是依賴倒置原則的最好體現(xiàn)
迪米特法則(LOD, Law Of Demeter) / 最小知道原則 (LKP,Least Knowledge Principle)
-
定義: 一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象有盡可能少的了解山害。
- 也就是說(shuō)纠俭,如果兩個(gè)類(lèi)不必彼此直接通信,那么這兩個(gè)類(lèi)就不應(yīng)當(dāng)發(fā)生直接的相互作用浪慌。
-
迪米特法則應(yīng)用:
- 外觀模式(Facade)
- 中介者模式(Mediator)
- 匿名對(duì)象
優(yōu)點(diǎn): 使對(duì)象之間的耦合降到最底冤荆,從而使得類(lèi)具有很好的可讀性和可維護(hù)性。
設(shè)計(jì)基本原則特點(diǎn)總結(jié)
- 單一職責(zé)原則主要說(shuō)明:類(lèi)的職責(zé)要單一
- 開(kāi)閉原則講述的是:對(duì)擴(kuò)展開(kāi)放权纤,對(duì)修改關(guān)閉
- 里氏替換原則強(qiáng)調(diào):不要破壞繼承體系
- 接口隔離原則講解:設(shè)計(jì)接口的時(shí)候要精簡(jiǎn)
- 依賴倒置原則描述要:面向接口編程
- 迪米特法則告訴我們:要降低耦合
面向?qū)ο蟮娜筇卣?/h4>
- 面向?qū)ο缶幊趟枷胫饕腥筇卣鞯黾颍謩e是:封裝,繼承 和 多態(tài)妖碉。
-
封裝 是指把類(lèi)中的細(xì)節(jié)進(jìn)行包裝涌庭,對(duì)外提供定義好的接口。封裝對(duì)實(shí)現(xiàn)細(xì)節(jié)進(jìn)行隱藏欧宜,使用者需要通過(guò)規(guī)定的訪問(wèn)來(lái)訪問(wèn)數(shù)據(jù)坐榆,這樣避免了使用者進(jìn)行不合理的賦值操作。
-
繼承 是使用已存在的類(lèi)定義作為基礎(chǔ)建立新類(lèi)的技術(shù)冗茸,新類(lèi)的定義可增加新的數(shù)據(jù)或新的功能席镀,也可以用父類(lèi)的功能匹中,但不能選擇性的繼承父類(lèi)。在繼承中豪诲,子類(lèi)擁有父類(lèi)非 private 的屬性和方法顶捷;子類(lèi)可以擁有自己的屬性和方法,即子類(lèi)可以對(duì)父類(lèi)進(jìn)行擴(kuò)展屎篱;子類(lèi)可以用自己的方式實(shí)現(xiàn)父類(lèi)的方法服赎。繼承使得系統(tǒng)在變化中有了延續(xù)性,同時(shí)繼承也是封裝過(guò)程中可變的因素交播,通過(guò)繼承還可以縮小代碼量重虑。
-
多態(tài) 是指允許不同的子類(lèi)類(lèi)型對(duì)同一消息做出不同的行為。多態(tài)可以大量減少代碼量的同時(shí)秦士,提高代碼的維護(hù)性和擴(kuò)展性缺厉。
什么是 MVC 設(shè)計(jì)模式?
- 模型-視圖-控制器(Model-View-Controller隧土,MVC)是一種廣泛應(yīng)用于用戶交互應(yīng)用程序中的軟件設(shè)計(jì)模式提针。iOS 中的 MVC 將軟件系統(tǒng)分為 Model、View曹傀、Controller 三部分辐脖,其中
-
Model 對(duì)象 封裝了應(yīng)用程序的數(shù)據(jù),并定義操控和處理該數(shù)據(jù)的邏輯和運(yùn)算卖毁;
-
View 對(duì)象 是應(yīng)用程序中用戶可以看見(jiàn)的對(duì)象揖曾,其主要目的是顯示來(lái)自應(yīng)用程序 Model 對(duì)象的數(shù)據(jù),并使該數(shù)據(jù)可被編輯亥啦;
-
Controller 對(duì)象 在應(yīng)用程序的一個(gè)或多個(gè) View 對(duì)象和一個(gè)或多個(gè) Model 對(duì)象之間充當(dāng)媒介炭剪,Controller 可以直接訪問(wèn) Model,Model 通過(guò) Notification 和 KVO 機(jī)制與 Controller 間接通信翔脱;Controller 也可以直接控制View奴拦,View 通過(guò) action 向 Controller 報(bào)告事件的發(fā)生,但 Model 和 View 不能互相通信届吁。
什么是 MVVM错妖?主要目的是什么?有哪些優(yōu)點(diǎn)疚沐?
- MVVM暂氯,為 Model-View-ViewModel 的簡(jiǎn)寫(xiě),本質(zhì)是 MVC 設(shè)計(jì)模式的改進(jìn)版亮蛔,將視圖處理邏輯從 C 中剝離出來(lái)給 V痴施,剩下的業(yè)務(wù)邏輯部分被稱(chēng)做 View-Model。MVVM 模式和 MVC 模式一樣,主要目的是分離視圖和模型辣吃,其優(yōu)點(diǎn)如下:
-
低耦合:View 可以獨(dú)立于 Model 變化和修改动遭,一個(gè) ViewModel 可以綁定到不同的 View 上,當(dāng) View 變化的時(shí)候神得,Model 可以不變厘惦,當(dāng) Model 變化的時(shí)候 View 也可以不變;
-
可重用性:如果把一些視圖邏輯放在一個(gè) ViewModel 里面哩簿,則很多 view 可重用這段視圖邏輯宵蕉;
-
獨(dú)立開(kāi)發(fā):開(kāi)發(fā)人員可以專(zhuān)注于業(yè)務(wù)邏輯和數(shù)據(jù)的開(kāi)發(fā)(ViewModel),設(shè)計(jì)人員可以專(zhuān)注于頁(yè)面設(shè)計(jì)节榜;
-
可測(cè)試:界面素來(lái)是較難于測(cè)試的国裳,而現(xiàn)在測(cè)試可以針對(duì) ViewModel 來(lái)寫(xiě)。
二全跨、內(nèi)存管理
-
規(guī)則
- 在iOS中,使用 “引用計(jì)數(shù)” 來(lái)管理OC對(duì)象的內(nèi)存
- 新創(chuàng)建的OC對(duì)象亿遂,引用計(jì)數(shù)是1浓若;
- 調(diào)用retain會(huì)讓OC對(duì)象的引用計(jì)數(shù)+1,調(diào)用release會(huì)讓OC對(duì)象的引用計(jì)數(shù)-1
- 當(dāng)引用計(jì)數(shù)減為0蛇数,OC對(duì)象就會(huì)銷(xiāo)毀挪钓,釋放占用的內(nèi)存空間
- 當(dāng)調(diào)用 alloc、new耳舅、copy碌上、mutableCopy 方法返回了一個(gè)對(duì)象,在不需要這個(gè)對(duì)象時(shí)浦徊,要調(diào)用
- release或者aoturelease釋放
引用計(jì)數(shù)怎么存儲(chǔ)馏予?
- 可以直接存儲(chǔ)在isa指針中
- 如果不夠存儲(chǔ)的話后雷,會(huì)存儲(chǔ)在SideTable結(jié)構(gòu)體的refcnts散列表中
struct SideTable {
spinlock_t stock;
RefcountMap refcnts; // 存放著對(duì)象引用計(jì)數(shù)的散列表
weak_table_t weak_table;
}
ARC具體為引用計(jì)數(shù)做了哪些工作召川?
- 編譯階段自動(dòng)添加代碼
- ARC是LLVM編譯器和Runtime系統(tǒng)相互協(xié)作的一個(gè)結(jié)果
- 編譯器幫我們實(shí)現(xiàn)內(nèi)存管理相關(guān)的代碼
- Runtime在程序運(yùn)行過(guò)程中處理弱引用
深拷貝與淺拷貝
-
概念:
-
深拷貝:內(nèi)容拷貝,產(chǎn)生新的對(duì)象
-
淺拷貝:指針拷貝湃鹊,沒(méi)有產(chǎn)生新的對(duì)象冕香,原對(duì)象的引用計(jì)數(shù)+1
-
完全拷貝:深拷貝的一種蛹尝,能拷貝多層內(nèi)容(使用歸解檔技術(shù))
-
執(zhí)行結(jié)果:
-
copy:不可變拷貝,產(chǎn)生不可變副本
-
mutableCopy:可變拷貝悉尾,產(chǎn)生可變副本
-
準(zhǔn)則:不可變對(duì)象的copy方法是淺拷貝突那,其余都是深拷貝??????????
-
原因:
- 它是不可變對(duì)象,沒(méi)有必要拷貝一份出來(lái)构眯,指向同一塊地址還節(jié)省內(nèi)存
- 不可變對(duì)象調(diào)用copy返回他本身愕难,不可變對(duì)象copy就相當(dāng)于是retain
對(duì)象的拷貝
- 遵守協(xié)議
(<NSCopying, NSMutableCopying>)
- 實(shí)現(xiàn)協(xié)議方法
- (id)copyWithZone:(NSZone *)zone {
Person *person = [Person allocWithZone:zone];
person.name = self.name;
return person;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
Person *person = [Person allocWithZone:zone];
person.name = self.name;
return person;
}
集合對(duì)象的拷貝
- 對(duì)于集合類(lèi)的可變對(duì)象來(lái)說(shuō),深拷貝并非嚴(yán)格意義上的深復(fù)制,只能算是單層深復(fù)制
- 即雖然新開(kāi)辟了內(nèi)存地址务漩,但是存放在內(nèi)存上的值(也就是數(shù)組里的元素仍然之鄉(xiāng)員數(shù)組元素值拄衰,并沒(méi)有另外復(fù)制一份),這就叫做單層深復(fù)制
- 對(duì)于集合類(lèi)的對(duì)象如何實(shí)現(xiàn)每一層都深拷貝呢饵骨?
initWithArray:copyItems
2翘悉、歸檔解檔技術(shù)
#import <Foundation/Foundation.h>
@interface Person : NSObject <NSCoding>
@property (nonatomic, copy) NSString *name;
@end
#import "Person.h"
@implementation Person
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self.name = [aDecoder decodeObjectForKey:@"name"];
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.name forKey:@"name"];
}
@end
自動(dòng)釋放池
-
底層結(jié)構(gòu):AutoreleasPool是通過(guò)以AutoreleasePoolPage為結(jié)點(diǎn)的 “雙向鏈表” 來(lái)實(shí)現(xiàn)的
-
AutoreleasPool運(yùn)行的三個(gè)過(guò)程:
- objc_autoreleasePoolPush()
- [objc autorelease]
- objc_autoreleasePoolPop(void *)
屬性修飾詞:Copy、Strong居触、Weak妖混、Assign
-
copy:
- 會(huì)在內(nèi)存里拷貝一份對(duì)象,兩個(gè)指針指向不同的內(nèi)存地址轮洋。
- 一般用來(lái)修飾NSString等有對(duì)應(yīng)可變類(lèi)型的對(duì)象制市,因?yàn)樗麄冇锌赡芎蛯?duì)應(yīng)的可變類(lèi)型(NSMutableString)之間進(jìn)行賦值操作,為確北子瑁可變對(duì)象變化時(shí)祥楣,對(duì)象中的字符串不被修改 ,應(yīng)該在設(shè)置屬性時(shí)拷貝一份汉柒。
- 而若用strong修飾误褪,如果可變對(duì)象變化,對(duì)象中的字符串屬性也會(huì)跟著變化碾褂。
-
strong:
- ARC下的strong等同于MRC下的retain都會(huì)把對(duì)象引用計(jì)數(shù)加1
-
weak:
- 修飾Object類(lèi)型兽间,修飾的對(duì)象在釋放后,指針地址會(huì)被置為nil正塌,是一種弱引用
- 在ARC環(huán)境下嘀略,為避免循環(huán)引用,往往會(huì)把delegate屬性用weak修飾
- weak和strong不同的是:當(dāng)一個(gè)對(duì)象不再有strong類(lèi)型的指針指向它的時(shí)候乓诽,它就會(huì)被釋放帜羊,即使還有weak型指針指向它,那么這些weak型指針也將被清除问裕。
-
assign:
- 用于對(duì)基本數(shù)據(jù)類(lèi)型進(jìn)行賦值操作逮壁,不更改引用計(jì)數(shù)
- 也可以用來(lái)修飾對(duì)象,但是被assign修飾的對(duì)象在釋放后粮宛,指針的地址還是存在的窥淆,指針并沒(méi)有被置為nil,成為野指針?
- 之所以可以修飾基本數(shù)據(jù)類(lèi)型巍杈,因?yàn)榛緮?shù)據(jù)類(lèi)型一般分配在棧上忧饭,棧的內(nèi)存會(huì)由系統(tǒng)自動(dòng)處理,不會(huì)造成野指針筷畦。
block屬性為什么需要用copy來(lái)修飾词裤?
- 因?yàn)樵贛RC下刺洒,block在創(chuàng)建的時(shí)候,它的內(nèi)存是分配在棧(stack)上的吼砂,而不是在堆(heap)上逆航,可能被隨時(shí)回收。
- 他本身的作于域是屬于創(chuàng)建時(shí)候的作用域渔肩,一旦在創(chuàng)建時(shí)候的作用域外面調(diào)用block將導(dǎo)致程序崩潰因俐。
- 通過(guò)copy可以把block拷貝(copy)到堆,保證block的聲明域外使用周偎。
- 在ARC下寫(xiě)不寫(xiě)都行抹剩,編譯器會(huì)自動(dòng)對(duì)block進(jìn)行copy操作。
代理為什么使用weak修飾蓉坎?
- weak指明該對(duì)象并不負(fù)責(zé)保持delegate這個(gè)對(duì)象澳眷,delegate的銷(xiāo)毀由外部控制;
- 如果用strong修飾蛉艾,強(qiáng)引用后外界不能銷(xiāo)毀delegate對(duì)象钳踊,會(huì)導(dǎo)致循環(huán)引用;
為什么NSMutableArray一般不用copy修飾勿侯?
- (void)setData:(NSMutableArray *)data {
if (_data != data) {
[_data release];
_data = [data copy];
}
}
- 封裝 是指把類(lèi)中的細(xì)節(jié)進(jìn)行包裝涌庭,對(duì)外提供定義好的接口。封裝對(duì)實(shí)現(xiàn)細(xì)節(jié)進(jìn)行隱藏欧宜,使用者需要通過(guò)規(guī)定的訪問(wèn)來(lái)訪問(wèn)數(shù)據(jù)坐榆,這樣避免了使用者進(jìn)行不合理的賦值操作。
- 繼承 是使用已存在的類(lèi)定義作為基礎(chǔ)建立新類(lèi)的技術(shù)冗茸,新類(lèi)的定義可增加新的數(shù)據(jù)或新的功能席镀,也可以用父類(lèi)的功能匹中,但不能選擇性的繼承父類(lèi)。在繼承中豪诲,子類(lèi)擁有父類(lèi)非 private 的屬性和方法顶捷;子類(lèi)可以擁有自己的屬性和方法,即子類(lèi)可以對(duì)父類(lèi)進(jìn)行擴(kuò)展屎篱;子類(lèi)可以用自己的方式實(shí)現(xiàn)父類(lèi)的方法服赎。繼承使得系統(tǒng)在變化中有了延續(xù)性,同時(shí)繼承也是封裝過(guò)程中可變的因素交播,通過(guò)繼承還可以縮小代碼量重虑。
- 多態(tài) 是指允許不同的子類(lèi)類(lèi)型對(duì)同一消息做出不同的行為。多態(tài)可以大量減少代碼量的同時(shí)秦士,提高代碼的維護(hù)性和擴(kuò)展性缺厉。
- Model 對(duì)象 封裝了應(yīng)用程序的數(shù)據(jù),并定義操控和處理該數(shù)據(jù)的邏輯和運(yùn)算卖毁;
- View 對(duì)象 是應(yīng)用程序中用戶可以看見(jiàn)的對(duì)象揖曾,其主要目的是顯示來(lái)自應(yīng)用程序 Model 對(duì)象的數(shù)據(jù),并使該數(shù)據(jù)可被編輯亥啦;
- Controller 對(duì)象 在應(yīng)用程序的一個(gè)或多個(gè) View 對(duì)象和一個(gè)或多個(gè) Model 對(duì)象之間充當(dāng)媒介炭剪,Controller 可以直接訪問(wèn) Model,Model 通過(guò) Notification 和 KVO 機(jī)制與 Controller 間接通信翔脱;Controller 也可以直接控制View奴拦,View 通過(guò) action 向 Controller 報(bào)告事件的發(fā)生,但 Model 和 View 不能互相通信届吁。
- 低耦合:View 可以獨(dú)立于 Model 變化和修改动遭,一個(gè) ViewModel 可以綁定到不同的 View 上,當(dāng) View 變化的時(shí)候神得,Model 可以不變厘惦,當(dāng) Model 變化的時(shí)候 View 也可以不變;
- 可重用性:如果把一些視圖邏輯放在一個(gè) ViewModel 里面哩簿,則很多 view 可重用這段視圖邏輯宵蕉;
- 獨(dú)立開(kāi)發(fā):開(kāi)發(fā)人員可以專(zhuān)注于業(yè)務(wù)邏輯和數(shù)據(jù)的開(kāi)發(fā)(ViewModel),設(shè)計(jì)人員可以專(zhuān)注于頁(yè)面設(shè)計(jì)节榜;
- 可測(cè)試:界面素來(lái)是較難于測(cè)試的国裳,而現(xiàn)在測(cè)試可以針對(duì) ViewModel 來(lái)寫(xiě)。
- 在iOS中,使用 “引用計(jì)數(shù)” 來(lái)管理OC對(duì)象的內(nèi)存
- 新創(chuàng)建的OC對(duì)象亿遂,引用計(jì)數(shù)是1浓若;
- 調(diào)用retain會(huì)讓OC對(duì)象的引用計(jì)數(shù)+1,調(diào)用release會(huì)讓OC對(duì)象的引用計(jì)數(shù)-1
- 當(dāng)引用計(jì)數(shù)減為0蛇数,OC對(duì)象就會(huì)銷(xiāo)毀挪钓,釋放占用的內(nèi)存空間
- 當(dāng)調(diào)用 alloc、new耳舅、copy碌上、mutableCopy 方法返回了一個(gè)對(duì)象,在不需要這個(gè)對(duì)象時(shí)浦徊,要調(diào)用
- release或者aoturelease釋放
struct SideTable {
spinlock_t stock;
RefcountMap refcnts; // 存放著對(duì)象引用計(jì)數(shù)的散列表
weak_table_t weak_table;
}
- 編譯器幫我們實(shí)現(xiàn)內(nèi)存管理相關(guān)的代碼
- Runtime在程序運(yùn)行過(guò)程中處理弱引用
概念:
- 深拷貝:內(nèi)容拷貝,產(chǎn)生新的對(duì)象
- 淺拷貝:指針拷貝湃鹊,沒(méi)有產(chǎn)生新的對(duì)象冕香,原對(duì)象的引用計(jì)數(shù)+1
- 完全拷貝:深拷貝的一種蛹尝,能拷貝多層內(nèi)容(使用歸解檔技術(shù))
執(zhí)行結(jié)果:
- copy:不可變拷貝,產(chǎn)生不可變副本
- mutableCopy:可變拷貝悉尾,產(chǎn)生可變副本
準(zhǔn)則:不可變對(duì)象的copy方法是淺拷貝突那,其余都是深拷貝??????????
-
原因:
- 它是不可變對(duì)象,沒(méi)有必要拷貝一份出來(lái)构眯,指向同一塊地址還節(jié)省內(nèi)存
- 不可變對(duì)象調(diào)用copy返回他本身愕难,不可變對(duì)象copy就相當(dāng)于是retain
(<NSCopying, NSMutableCopying>)
- (id)copyWithZone:(NSZone *)zone {
Person *person = [Person allocWithZone:zone];
person.name = self.name;
return person;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
Person *person = [Person allocWithZone:zone];
person.name = self.name;
return person;
}
initWithArray:copyItems
2翘悉、歸檔解檔技術(shù)#import <Foundation/Foundation.h>
@interface Person : NSObject <NSCoding>
@property (nonatomic, copy) NSString *name;
@end
#import "Person.h"
@implementation Person
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self.name = [aDecoder decodeObjectForKey:@"name"];
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.name forKey:@"name"];
}
@end
- objc_autoreleasePoolPush()
- [objc autorelease]
- objc_autoreleasePoolPop(void *)
copy:
- 會(huì)在內(nèi)存里拷貝一份對(duì)象,兩個(gè)指針指向不同的內(nèi)存地址轮洋。
- 一般用來(lái)修飾NSString等有對(duì)應(yīng)可變類(lèi)型的對(duì)象制市,因?yàn)樗麄冇锌赡芎蛯?duì)應(yīng)的可變類(lèi)型(NSMutableString)之間進(jìn)行賦值操作,為確北子瑁可變對(duì)象變化時(shí)祥楣,對(duì)象中的字符串不被修改 ,應(yīng)該在設(shè)置屬性時(shí)拷貝一份汉柒。
- 而若用strong修飾误褪,如果可變對(duì)象變化,對(duì)象中的字符串屬性也會(huì)跟著變化碾褂。
strong:
- ARC下的strong等同于MRC下的retain都會(huì)把對(duì)象引用計(jì)數(shù)加1
weak:
- 修飾Object類(lèi)型兽间,修飾的對(duì)象在釋放后,指針地址會(huì)被置為nil正塌,是一種弱引用
- 在ARC環(huán)境下嘀略,為避免循環(huán)引用,往往會(huì)把delegate屬性用weak修飾
- weak和strong不同的是:當(dāng)一個(gè)對(duì)象不再有strong類(lèi)型的指針指向它的時(shí)候乓诽,它就會(huì)被釋放帜羊,即使還有weak型指針指向它,那么這些weak型指針也將被清除问裕。
assign:
- 用于對(duì)基本數(shù)據(jù)類(lèi)型進(jìn)行賦值操作逮壁,不更改引用計(jì)數(shù)
- 也可以用來(lái)修飾對(duì)象,但是被assign修飾的對(duì)象在釋放后粮宛,指針的地址還是存在的窥淆,指針并沒(méi)有被置為nil,成為野指針?
- 之所以可以修飾基本數(shù)據(jù)類(lèi)型巍杈,因?yàn)榛緮?shù)據(jù)類(lèi)型一般分配在棧上忧饭,棧的內(nèi)存會(huì)由系統(tǒng)自動(dòng)處理,不會(huì)造成野指針筷畦。
- (void)setData:(NSMutableArray *)data {
if (_data != data) {
[_data release];
_data = [data copy];
}
}
拷貝完成后:可變數(shù)組->不可變數(shù)組箍土,在外操作時(shí)(添加、刪除等)會(huì)存在問(wèn)題
什么是“僵尸對(duì)象”?
- 一個(gè)OC對(duì)象引用計(jì)數(shù)為0被釋放后就變成僵尸對(duì)象罐监,僵尸對(duì)象的內(nèi)存已經(jīng)被系統(tǒng)回收
- 雖然可能該對(duì)象還存在,數(shù)據(jù)依然在內(nèi)存中瞒爬,但僵尸對(duì)象已經(jīng)是不穩(wěn)定對(duì)象了弓柱,不可以再訪問(wèn)或者使用
- 它的內(nèi)存是隨時(shí)可能被別的對(duì)象申請(qǐng)而占用的
有哪些情況會(huì)出現(xiàn)內(nèi)存泄漏
- block循環(huán)引用
- delegate循環(huán)引用問(wèn)題
- NSTimer循環(huán)引用
- 地圖類(lèi)處理
- 線程保活target:self
- (void)dealloc底層執(zhí)行了什么侧但?
- (void)dealloc {
_objc_rootDealloc(self);
}
void _objc_rootDealloc(id obj) {
ASSERT(obj);
obj->rootDealloc();
}
inline void objc_object::rootDealloc() {
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer && // 無(wú)優(yōu)化過(guò)isa指針
!isa.weakly_referenced && // 無(wú)弱引用
!isa.has_assoc && // 無(wú)關(guān)聯(lián)對(duì)象
!isa.has_cxx_dtor && // 無(wú)cxx析構(gòu)函數(shù)
!isa.has_sidetable_rc)) { // 不存在引用計(jì)數(shù)器是否過(guò)大無(wú)法存儲(chǔ)在isa中(使用 sidetable 來(lái)存儲(chǔ)引用計(jì)數(shù))
// 直接釋放
assert(!sidetable_present());
free(this);
} else {
// 下一步
object_dispose((id)this);
}
}
// 如果不能快速釋放矢空,則調(diào)用 object_dispose()方法,做下一步的處理
static id _object_dispose(id anObject) {
if (anObject==nil) return nil;
objc_destructInstance(anObject);
anObject->initIsa(_objc_getFreedObjectClass ());
free(anObject);
return nil;
}
void *objc_destructInstance(id obj) {
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor(); // 是否存在析構(gòu)函數(shù)
bool assoc = obj->hasAssociatedObjects(); // 是否有關(guān)聯(lián)對(duì)象
// This order is important.
if (cxx) object_cxxDestruct(obj); // 銷(xiāo)毀成員變量
if (assoc) _object_remove_assocations(obj); // 釋放動(dòng)態(tài)綁定的對(duì)象
obj->clearDeallocating();
}
return obj;
}
/*
* clearDeallocating一共做了兩件事
*
* 將對(duì)象弱引用表清空禀横,即將弱引用該對(duì)象的指針置為nil
* 2屁药、清空引用計(jì)數(shù)表
* - 當(dāng)一個(gè)對(duì)象的引用計(jì)數(shù)值過(guò)大(超過(guò)255)時(shí),引用計(jì)數(shù)會(huì)存儲(chǔ)在一個(gè)叫 SideTable 的屬性中
* - 此時(shí)isa的 has_sidetable_rc 值為1柏锄,這就是為什么弱引用不會(huì)導(dǎo)致循環(huán)引用的原因
*/
inline void objc_object::clearDeallocating() {
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// Slow path for non-pointer isa with weak refs and/or side table data.
clearDeallocating_slow();
}
assert(!sidetable_present());
}
三酿箭、多線程
GCD
GCD核心概念:「任務(wù)」、「隊(duì)列」
-
任務(wù):
概念:指操作趾娃,線程中執(zhí)行的那段代碼缭嫡,GCD主要放在block中;
執(zhí)行任務(wù)的方式:「同步執(zhí)行」抬闷、「異步執(zhí)行」妇蛀;
區(qū)別:是否等待隊(duì)列的任務(wù)執(zhí)行結(jié)束耕突,是否具備開(kāi)啟新縣城的能力;-
同步執(zhí)行(sync)
- 同步添加任務(wù)到指定隊(duì)列中评架,在添加的任務(wù)執(zhí)行結(jié)束之前眷茁,會(huì)一直等待,直到隊(duì)列里面的任務(wù)完成之后再繼續(xù)執(zhí)行
- 只能在當(dāng)前線程中執(zhí)行任務(wù)纵诞,不具備開(kāi)啟新線程的能力
-
異步執(zhí)行(async)
- 異步添加任務(wù)到指定隊(duì)列中上祈,不會(huì)做任何等待,可以繼續(xù)執(zhí)行任務(wù)
- 可以在新的線程中執(zhí)行任務(wù)挣磨,具備開(kāi)啟新縣城的能力
- 異步執(zhí)行雖然具有開(kāi)啟新線程的能力雇逞,但不一定開(kāi)啟新線程。(與任務(wù)指定的隊(duì)列類(lèi)型有關(guān))
-
-
隊(duì)列(Dispatch Queue)
概念:執(zhí)行任務(wù)的等待隊(duì)列茁裙,即用來(lái)存放任務(wù)的隊(duì)列
結(jié)構(gòu):特殊的線性表塘砸,采用FIFO(先進(jìn)先出)原則。即每讀取一個(gè)任務(wù)晤锥,則從隊(duì)列中釋放一個(gè)任務(wù)-
串行隊(duì)列:(Serial Dispatch Queue)
- 每次只有一個(gè)任務(wù)被執(zhí)行掉蔬,任務(wù)依次執(zhí)行(只開(kāi)啟一個(gè)線程,一個(gè)任務(wù)執(zhí)行完成后矾瘾,再執(zhí)行下一個(gè)任務(wù))
-
并發(fā)隊(duì)列:(Concurrent Dispatch Queue)
- 可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行女轿。(可以開(kāi)啟多個(gè)線程,并且同時(shí)執(zhí)行任務(wù))
- 并發(fā)隊(duì)列的「并發(fā)」功能只有在異步(dispatch_async)方法下才有效
-
-
GCD使用步驟
- 創(chuàng)建一個(gè)隊(duì)列(串行隊(duì)列/并發(fā)隊(duì)列)
- 將任務(wù)追加到任務(wù)的等待隊(duì)列中壕翩,然后系統(tǒng)就會(huì)根據(jù)任務(wù)類(lèi)型執(zhí)行任務(wù)(同步執(zhí)行/異步執(zhí)行)
-
死鎖條件:
- 使用sync函數(shù)往當(dāng)前串行隊(duì)列中添加任務(wù)蛉迹,會(huì)卡住當(dāng)前的串行隊(duì)列。
如何打造線程安全的NSMutableArray放妈?
- 線程鎖:使用線程鎖在對(duì)數(shù)組讀寫(xiě)時(shí)候加鎖
- 派發(fā)隊(duì)列:
《Effective Objective 2.0》中41條提出的觀點(diǎn)北救,串行同步:將讀取和寫(xiě)入都安排在同一個(gè)隊(duì)列里,可保證數(shù)據(jù)同步芜抒。
如何異步下載多張小圖最后合成一張大圖珍策?
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{ /*加載圖片1 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片2 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片3 */ });
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 合并圖片
});
什么是線程安全?
- 多線程操作過(guò)程中往往都是多個(gè)線程并發(fā)執(zhí)行的宅倒,因此同一個(gè)資源可能被多個(gè)線程同時(shí)訪問(wèn)攘宙,造成資源搶奪。
- 線程安全就是多條線程同時(shí)訪問(wèn)一段代碼拐迁,不會(huì)造成數(shù)據(jù)混亂的情況
如何設(shè)置常駐線程蹭劈?????
- 為當(dāng)前線程開(kāi)啟一個(gè) RunLoop (第一次調(diào)用 [NSRunLoop currentRunLoop] 方法時(shí)
實(shí)際是會(huì)先去創(chuàng)建一個(gè) RunLoop ) - 向當(dāng)前 RunLoop 中添加一個(gè) Port/Source 等維持 RunLoop 的事件循環(huán)(如果
RunLoop 的 mode 中一個(gè) item 都沒(méi)有, RunLoop 會(huì)退出) - 啟動(dòng)該 RunLoop
在異步線程發(fā)送通知线召,在主線程接收通知链方。會(huì)不會(huì)有什么問(wèn)題?
GCD線程是如何調(diào)度的
如何實(shí)現(xiàn)多個(gè)任務(wù)執(zhí)行完后灶搜,再統(tǒng)一處理祟蚀?
- 同步阻塞
- 柵欄函數(shù)
- 線程組
線程和線程之間如何通信工窍?
-
線程通信的表現(xiàn):
- 1個(gè)線程傳遞數(shù)據(jù)給另1個(gè)線程
- 在1個(gè)線程中執(zhí)行完特定任務(wù)后,轉(zhuǎn)到另1個(gè)線程繼續(xù)執(zhí)行任務(wù)
線程間通信常用方法:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
示例:
- (void)viewDidLoad {
[super viewDidLoad];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// 在子線程中調(diào)用download方法下載圖片
[self performSelectorInBackground:@selector(download) withObject:nil];
}
- (void)download {
// 1.根據(jù)URL網(wǎng)絡(luò)中下載圖片
NSURL *urlstr=[NSURL URLWithString:@"fdsf"];
// 2前酿、把圖片轉(zhuǎn)換為二進(jìn)制的數(shù)據(jù), 這一行操作會(huì)比較耗時(shí)
NSData *data=[NSData dataWithContentsOfURL:urlstr];
// 3患雏、把數(shù)據(jù)轉(zhuǎn)換成圖片
UIImage *image=[UIImage imageWithData:data];
// 4、回到主線程中設(shè)置圖片
[self performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:NO];
}
//設(shè)置顯示圖片
- (void)settingImage:(UIImage *)image {
self.iconView.image=image;
}
談?wù)刟tomic的實(shí)現(xiàn)機(jī)制罢维,為什么不能保證絕對(duì)線程安全淹仑?
-
實(shí)現(xiàn)機(jī)制:
- 編譯器自動(dòng)生成
getter/setter
方法中添加鎖保證線程安全
- 編譯器自動(dòng)生成
-
為什么不能保證絕對(duì)安全?
- 在
getter/setter
中加鎖肺孵,僅保證存取時(shí)線程安全匀借,不會(huì)讓你拿到一個(gè)崩潰的值 - 無(wú)法保證對(duì)容器的修改是線程安全的,例:假設(shè)屬性是可變?nèi)萜?code>(@property (atomic) NSMutableArray *array)時(shí)
- 重寫(xiě)
getter/setter
方法時(shí)平窘,只能依靠自己在getter/setter
中保證線程安全
- 在
- (void)setCurrentImage:(UIImage *)currentImage {
if (_currentImage != currentImage) {
[_currentImage release];
_currentImage = [currentImage retain];
}
}
- (UIImage *)currentImage {
return _currentImage;
}
- (void)setCurrentImage:(UIImage *)currentImage {
@synchronized(self) {
if (_currentImage != currentImage) {
[_currentImage release];
_currentImage = [currentImage retain];
}
}
}
- (UIImage *)currentImage {
@synchronized(self) {
return _currentImage;
}
}
進(jìn)程和線程的區(qū)別
-
區(qū)別:
- 一個(gè)線程只能屬于一個(gè)進(jìn)程.
- 一個(gè)進(jìn)程可以有多個(gè)線程吓肋,但至少有一個(gè)線程。
- 線程是操作系統(tǒng)可識(shí)別的最小執(zhí)行和調(diào)度單位瑰艘。
-
資源分配:
- 資源分配給進(jìn)程是鬼,同一進(jìn)程的所有線程共享該進(jìn)程的所有資源。
- 同一進(jìn)程中的多個(gè)線程共享代碼段紫新、數(shù)據(jù)段均蜜、擴(kuò)展段。
- 但是每個(gè)線程擁有自己的棧段芒率,棧段又叫運(yùn)行時(shí)段囤耳,用來(lái)存放所有局部變量和臨時(shí)變量。
Notification與線程相關(guān)
- 在多線程應(yīng)用中偶芍,Notification在哪個(gè)線程中post紫皇,就在哪個(gè)線程中被轉(zhuǎn)發(fā),而不一定是在注冊(cè)觀察者的那個(gè)線程中腋寨。
- 換句話說(shuō)就是在哪個(gè)線程發(fā)送通知,就在哪個(gè)線程接受通知化焕。
如何實(shí)現(xiàn)在不同線程中post和轉(zhuǎn)發(fā)一個(gè)Notification萄窜?
重定向的實(shí)現(xiàn)思路:
- 自定義一個(gè)通知隊(duì)列(用數(shù)組類(lèi)型),讓它去維護(hù)那些我們需要重定向的Notification
- 我們?nèi)匀皇窍衿匠R粯尤プ?cè)一個(gè)通知的觀察者撒桨,當(dāng)Notification來(lái)了時(shí)查刻,先看看post這個(gè)Notification的線程是不是我們所期望的線程
- 如果不是,則將這個(gè)Notification存儲(chǔ)到我們的隊(duì)列中凤类,并發(fā)送一個(gè)信號(hào)(signal)到期望的線程中穗泵,來(lái)告訴這個(gè)線程需要處理一個(gè)Notification
- 指定的線程在收到信號(hào)后,將Notification從隊(duì)列中移除谜疤,并進(jìn)行處理
dispatch_once底層實(shí)現(xiàn)
線程鎖
-
線程鎖的作用:
- 我們?cè)谑褂枚嗑€程的時(shí)候佃延,多個(gè)線程可能會(huì)訪問(wèn)同一塊資源现诀,就很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全等問(wèn)題
- 這時(shí)候就需要我們保證每次只有一個(gè)線程訪問(wèn)這一塊資源
-
線程鎖類(lèi)型:
- 互斥鎖
- 自旋鎖
- 信號(hào)量
- 遞歸鎖
- atomic
互斥鎖
- 標(biāo)記用來(lái)保證在任一時(shí)刻,只能有一個(gè)線程訪問(wèn)對(duì)象
NSLock
@synchronized (self)
自旋鎖
- OSSpinLock(YYKit作者有一篇文章寫(xiě)它不安全履肃,可以自己研究一下)
- os_unfair_lock
信號(hào)量(Semaphore - dispatch_semaphore_t)
- 多線程環(huán)境下使用的一種設(shè)施仔沿,是可以用來(lái)保證兩個(gè)或多個(gè)關(guān)鍵代碼段不被并發(fā)調(diào)用
- 在進(jìn)入一個(gè)關(guān)鍵代碼段之前,線程必須獲取一個(gè)信號(hào)量尺棋;關(guān)鍵代碼段完成后封锉,該線程必須釋放信號(hào)量
- 其它想進(jìn)入該關(guān)鍵代碼段的線程必須等待直到第一個(gè)線程釋放信號(hào)量
遞歸鎖(NSRecursiveLock)
- 同一個(gè)線程可以多次加鎖,不會(huì)造成死鎖
atomic
- atomic 修飾的對(duì)象膘螟,系統(tǒng)會(huì)保證在其自動(dòng)生成的
getter/setter
方法中的操作是完整的成福,不受其他線程的影響
線程不安全
- 如果有另一個(gè)線程同時(shí)在調(diào)
[name release]
,那可能就會(huì)crash荆残,因?yàn)?release
不受getter/setter
操作的限制 - 這個(gè)屬性只能說(shuō)是讀/寫(xiě)安全的奴艾,但并不是線程安全的,因?yàn)閯e的線程還能進(jìn)行讀寫(xiě)之外的其他操作
四脊阴、Block相關(guān)
Block
-
block本質(zhì)
- block是將函數(shù)及其執(zhí)行上下文封裝起來(lái)的對(duì)象
關(guān)于block的截獲特性握侧,你是否有了解?block的截獲變量特性是怎樣的嘿期?
-
變量捕獲機(jī)制分析:
- 對(duì)于“基本數(shù)據(jù)類(lèi)型”的“局部變量”截獲其值
- 對(duì)于“對(duì)象”類(lèi)型的局部變量“連同所有權(quán)修飾符”一起截獲
- 以“指針形式”截獲局部靜態(tài)變量(指針傳遞)
- 不截獲全局變量品擎、靜態(tài)全局變量(直接訪問(wèn))
-
改外部變量必要條件
- 將auto從棧copy到堆
原因:棧中內(nèi)存管理是由系統(tǒng)管理,出了作用域就會(huì)被回收备徐,堆中才是可以由程序員管理
- 將auto從棧copy到堆
對(duì)棧上的block進(jìn)行copy之后萄传,假如在mrc環(huán)境下,內(nèi)存是否回泄漏蜜猾?
- copy操作之后秀菱,堆上的block沒(méi)有額外的成員變量指向它,正如我們alloc對(duì)象后蹭睡,沒(méi)有進(jìn)行release衍菱,造成內(nèi)存泄漏
為什么block會(huì)產(chǎn)生循環(huán)引用?
- 如果當(dāng)前block對(duì)當(dāng)前對(duì)象的某一成員變量進(jìn)行截獲肩豁,block會(huì)對(duì)當(dāng)前對(duì)象有一個(gè)強(qiáng)引用
- 而當(dāng)前block由于當(dāng)前對(duì)象對(duì)其有一個(gè)強(qiáng)引用脊串,產(chǎn)生了一個(gè)自循環(huán)引用的一個(gè)循環(huán)引用的問(wèn)題
Block不允許修改外部變量的值原因
block 本質(zhì)上是一個(gè)對(duì)象,block 的花括號(hào)區(qū)域是對(duì)象內(nèi)部的一個(gè)函數(shù)清钥,變量進(jìn)入 花括號(hào)琼锋,實(shí)際就是已經(jīng)進(jìn)入了另一個(gè)函數(shù)區(qū)域---改變了作用域。
在幾個(gè)作用域之間進(jìn)行切換時(shí)祟昭,如果不加上這樣的限制缕坎,變量的可維護(hù)性將大大降低。
比如想在block內(nèi)聲明了一個(gè)與外部同名的變量篡悟,此時(shí)是允許呢還是不允許呢谜叹?只有加上了這樣的限制匾寝,這樣的情景才能實(shí)現(xiàn)。
所以 Apple 在編譯器層面做了限制叉谜,如果在 block 內(nèi)部試圖修改 auto 變量(無(wú)修飾符)旗吁,那么直接編譯報(bào)錯(cuò)。
可以把編譯器的這種行為理解為:對(duì) block 內(nèi)部捕獲到的 auto 變量設(shè)置為只讀屬性---不允許直接修改停局。
如何實(shí)現(xiàn)對(duì)外部變量的捕獲很钓?
- 將變量設(shè)置為全局變量。原理:block內(nèi)外可直接訪問(wèn)全局變量
- 加 static (放在靜態(tài)存儲(chǔ)區(qū)/全局初始化區(qū))董栽。原理是block內(nèi)部對(duì)外部auto變量進(jìn)行指針捕獲
- 最優(yōu)解:使用__block 關(guān)鍵字
__block
- 將auto變量封裝為結(jié)構(gòu)體(對(duì)象)码倦,在結(jié)構(gòu)體內(nèi)部新建一個(gè)同名的auto變量
- block內(nèi)截獲該結(jié)構(gòu)體的指針
- 在block中使用自動(dòng)變量時(shí),使用指針指向的結(jié)構(gòu)體中的自動(dòng)變量
__block int var = 10;
void(^blk)(void) = ^{
var = 20;
};
blk();
轉(zhuǎn)換后的代碼:
struct __Block_byref_var_0 {
void *__isa;
__Block_byref_var_0 *__forwarding;
int __flags;
int __size;
int var; // 10 => 20 該結(jié)構(gòu)體持有相當(dāng)于原來(lái)自動(dòng)變量的成員變量
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_var_0 *var; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_var_0 *_var, int flags=0) : var(_var->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
block在修改NSMutableArray需不需要添加__block
- 不需要
- 原因:
- 當(dāng)變量是一個(gè)指針的時(shí)候锭碳,block里只是復(fù)制了一份這個(gè)指針袁稽,兩個(gè)指針指向同一個(gè)地址。
- 所以擒抛,在block里面對(duì)指針指向內(nèi)容做的修改推汽,在block外面也一樣生效。
block是如何捕獲局部變量的?
- block捕獲外界變量時(shí)歧沪,在內(nèi)部會(huì)自動(dòng)生成同一個(gè)屬性來(lái)保存
UIView動(dòng)畫(huà)中block回調(diào)里self要不要弱引用歹撒?
- 不需要,它不會(huì)造成循環(huán)引用诊胞,因?yàn)樗穷?lèi)方法暖夭。
- 之所以需要弱引用本身,是因?yàn)榕聦?duì)象之間產(chǎn)生循環(huán)引用撵孤,當(dāng)前控制器不可能強(qiáng)引用一個(gè)類(lèi)迈着,所以循環(huán)無(wú)法形成。
block里面會(huì)不會(huì)存在self為空的情況(weak strong的原理)邪码?
__weak typeof(self) weakself = self;
[self wj_refresh_addRefreshHeader:^{
__strong typeof(weakself) strongself = weakself;
[strongself.dataSource reloadDataWithCompletion:nil];
}];
- 有時(shí)候weakSelf在block里在執(zhí)行reloadDataWithCompletion還存在
- 但在執(zhí)行reloadDataWithCompletion前裕菠,可能會(huì)被釋放了
- 為了保證self在block執(zhí)行過(guò)程里一直存在,對(duì)他強(qiáng)引用strongSelf
__block與__weak的區(qū)別
- __block不管是ARC還是MRC模式下都可以使用闭专,可以修飾對(duì)象奴潘,還可以修飾基本數(shù)據(jù)類(lèi)型
- __weak只能在ARC模式下使用,也只能修飾對(duì)象(NSString)喻圃,不能修飾基本數(shù)據(jù)類(lèi)型(int)
- __block對(duì)象可以在block中被重新賦值,__weak不可以。
多層block嵌套如何使用weakSelf?
__weak typeof(self) weakself = self;
[self wj_refresh_addRefreshHeader:^{
__strong typeof(weakself) strongself = weakself;
__weak typeof(self) weakSelf2 = strongself;
[strongself.dataSource reloadDataWithCompletion:^(BOOL result) {
__strong typeof(self) strongSelf2 = weakSelf2;
}];
}];
Masonry對(duì)于block內(nèi)部引用self會(huì)不會(huì)造成循環(huán)引用摹蘑?
- 不會(huì)
- 這個(gè)block沒(méi)有copy免绿,是在棧上胧后,使用完直接釋放了瑞你,
- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
block(constraintMaker);
return [constraintMaker install];
}
__weak來(lái)解決block中的循環(huán)引用杠氢,還有別的方法嗎
- __block
- 將對(duì)象傳進(jìn)入修改
代理些楣、Block利弊
與委托代理模式的代碼相比昂勉,用block寫(xiě)出的代碼更為整潔
-
代理優(yōu)點(diǎn):
- 代理語(yǔ)法清晰浪册,可讀性高,易于維護(hù)
- 它減少代碼耦合性岗照,使事件監(jiān)聽(tīng)與事件處理分離
- 一個(gè)控制器可以實(shí)現(xiàn)多個(gè)代理村象,滿足自定義開(kāi)發(fā)需求,靈活性較高
-
代理缺點(diǎn):
- 實(shí)現(xiàn)代理的過(guò)程較繁瑣
- 跨層傳值時(shí)加大代碼的耦合性攒至,并且程序的層次結(jié)構(gòu)也變得混亂
- 當(dāng)多個(gè)對(duì)象同時(shí)傳值時(shí)不易區(qū)分厚者,導(dǎo)致代理易用性大大降低
-
block優(yōu)點(diǎn):
- 語(yǔ)法簡(jiǎn)潔,代碼可讀性和維護(hù)性較高
- 配合GCD優(yōu)秀的解決多線程問(wèn)題
-
block缺點(diǎn):
- Block中得代碼將自動(dòng)進(jìn)行一次retain操作库菲,容易造成內(nèi)存泄漏
- Block內(nèi)默認(rèn)引用為強(qiáng)引用,容易造成循環(huán)應(yīng)用
-
運(yùn)行成本:
- delegate運(yùn)行成本低志膀,block的運(yùn)行成本高
- block出棧需要將使用的數(shù)據(jù)從棧內(nèi)存拷貝到堆內(nèi)存熙宇,當(dāng)然對(duì)象的話就是假引用技術(shù),使用完block置nil才會(huì)消除
- delegate只是保存了一個(gè)對(duì)象的指針溉浙,直接回調(diào)烫止,沒(méi)有額外的消耗。就像c的函數(shù)指針放航,只多了一個(gè)查表動(dòng)作
五烈拒、Runtime
Runtime的相關(guān)術(shù)語(yǔ)
SEL、id广鳍、Class荆几、Method、IMP赊时、Cache吨铸、Property
介紹下runtime的內(nèi)存模型(isa、對(duì)象祖秒、類(lèi)诞吱、metaclass、結(jié)構(gòu)體的存儲(chǔ)信息等)
為什么要設(shè)計(jì)metaclass
class_copyIvarList & class_copyPropertyList區(qū)別
class_rw_t 和 class_ro_t 的區(qū)別
交互兩個(gè)方法的現(xiàn)實(shí)有什么風(fēng)險(xiǎn)竭缝?
六房维、Runloop
- 什么是RunLoop?
- RunLoop 實(shí)際上是一個(gè)對(duì)象
- 這個(gè)對(duì)象在循環(huán)中用來(lái)處理程序運(yùn)行過(guò)程中出現(xiàn)的各種事件(比如說(shuō)觸摸事件抬纸、UI刷新事件咙俩、定時(shí)器事件、Selector事件)
- 從而保持程序的持續(xù)運(yùn)行
- 在沒(méi)有事件處理的時(shí)候,會(huì)使線程進(jìn)入睡眠模式阿趁,從而節(jié)省 CPU 資源膜蛔,提高程序性能
- 應(yīng)用范疇
- 定時(shí)器(Timer)、PerformSelect
- GCD Async Main Queue
- 事件響應(yīng)脖阵、手勢(shì)識(shí)別皂股、界面刷新
- 網(wǎng)絡(luò)請(qǐng)求
- AutoreleasePool
- 基本應(yīng)用
- 保持程序的持續(xù)運(yùn)行
- 處理App中的各種事件(比如觸摸事件、定時(shí)器事件等)
- 節(jié)省CPU資源命黔,提高程序性能:該做事時(shí)做事呜呐,該休息時(shí)休息
- runloop和線程的關(guān)系
- 每條線程都有唯一的一個(gè)與之對(duì)應(yīng)的RunLoop對(duì)象
- RunLoop保存在一個(gè)全局的Dictionary里,線程作為key纷铣,RunLoop作為value
- 線程剛創(chuàng)建時(shí)并沒(méi)有RunLoop對(duì)象卵史,RunLoop會(huì)在第一次獲取它時(shí)創(chuàng)建
- RunLoop會(huì)在線程結(jié)束時(shí)銷(xiāo)毀
- 主線程的RunLoop已經(jīng)自動(dòng)獲取(創(chuàng)建)搜立,子線程默認(rèn)沒(méi)有開(kāi)啟RunLoop
/*
* 從字典中獲取以躯,如果沒(méi)有則直接創(chuàng)建
*/
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFSpinUnlock(&loopsLock);
if (!loop) {
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFSpinLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
__CFSpinUnlock(&loopsLock);
CFRelease(newLoop);
}
NSTimer相關(guān)
NSTimer準(zhǔn)嗎?如果不準(zhǔn)的話原因是什么啄踊?如何解決忧设?
- 原因:
- NSTimer的觸發(fā)時(shí)間到的時(shí)候,會(huì)在RunLoop中被檢測(cè)一次颠通;
- 如果在這一次的RunLoop中做了耗時(shí)的操作址晕,會(huì)處于阻塞狀態(tài)
- 時(shí)間超過(guò)了定時(shí)器的間隔時(shí)間,觸發(fā)時(shí)間就會(huì)推遲到下一個(gè)runloop周期
- 解決方法:
- 在子線程中創(chuàng)建timer顿锰,在子線程中進(jìn)行定時(shí)任務(wù)的操作谨垃,需要UI操作時(shí)切換回主線程進(jìn)行操作
- 使用CADisplayLink(一般用來(lái)做UI展示更新,同樣存在runloop卡頓問(wèn)題)
- 使用GCD定時(shí)器
使用NSTimer是如何處理循環(huán)引用的硼控?
使用類(lèi)方法
TODO(待填充);
談?wù)劤S玫娜N定時(shí)器優(yōu)缺點(diǎn)(NSTimer刘陶、CADisplayLink、GCD定時(shí)器)?????
- NSTimer和CADisplayLink依賴于RunLoop牢撼,如果RunLoop的任務(wù)過(guò)于繁重匙隔,可能會(huì)導(dǎo)致NSTimer不準(zhǔn)時(shí)
- 相比之下GCD的定時(shí)器會(huì)更加準(zhǔn)時(shí),因?yàn)镚CD不是依賴RunLoop熏版,而是由內(nèi)核決定
- CADisplayLink和NSTimer會(huì)對(duì)target產(chǎn)生強(qiáng)引用纷责,如果target又對(duì)它們產(chǎn)生強(qiáng)引用,那么就會(huì)引發(fā)循環(huán)引用
在viewWillDisappear或者viewDidDisappear方法中將 timer = nil撼短,是否還會(huì)造成循環(huán)引用再膳?
- 問(wèn)題一:如果只是想在離開(kāi)此頁(yè)時(shí)要釋放,進(jìn)入下一頁(yè)時(shí)不要釋放曲横,場(chǎng)景就不適用了
- runloop->timer喂柒;controller->timer
如何利用runloop監(jiān)控卡頓???
七、KVO
KVO基礎(chǔ)
KVO 的 全稱(chēng)Key-Value Observing,俗稱(chēng)“鍵值監(jiān)聽(tīng)”胳喷,可以用于某個(gè)對(duì)象屬性值的改變
iOS用什么方式實(shí)現(xiàn)對(duì)一個(gè)對(duì)象的KVO?(KVO的本質(zhì)是什么)
- 利用runtimeAPI動(dòng)態(tài)生成一個(gè)子類(lèi)(NSKVONotifying_XXXX)夭织,并且讓instance對(duì)象的isa指向這個(gè)全新的子類(lèi)
- 當(dāng)修改instance對(duì)象的屬性時(shí)吭露,會(huì)動(dòng)用Foundation的_NSSetXXXValueAndNotify函數(shù)
- willChangeValueForKey
- 父類(lèi)原來(lái)的setter方法
- didChangeValueForKey
- 內(nèi)部觸發(fā)監(jiān)聽(tīng)器(
ObserveValueForKeyPath:ofObject:change:context
)
如何手動(dòng)觸發(fā)KVO?
- 手動(dòng)調(diào)用
willChangeValueForKey
- 修改成員變量值
- 手動(dòng)調(diào)用
didChangeValueForKey
直接修改成員變量會(huì)觸發(fā)KVO么尊惰?
- 不會(huì)觸發(fā)KVO(原因看KVO的本質(zhì))
八讲竿、KVC
KVC基礎(chǔ)
KVC的全稱(chēng)是Key-Value Coding,俗稱(chēng)“鍵值編碼”弄屡,可以通過(guò)一個(gè)key來(lái)訪問(wèn)某個(gè)屬性
通過(guò)KVC修改屬性會(huì)出發(fā)KVO么题禀?
- 能觸發(fā)KVO()
- KVC在修改屬性時(shí),會(huì)調(diào)用
willChangeValueForKey:和didChangeValueForKey:
方法
KVC的賦值和取值過(guò)程是怎樣的膀捷?
- 賦值過(guò)程
- 首先會(huì)按照setKey迈嘹、_setKey的順序查找方法,找到方法全庸,直接調(diào)用方法并賦值秀仲;
- 未找到方法,則調(diào)用
+ (BOOL)accessInstanceVariablesDirectly;
- 若accessInstanceVariablesDirectly方法返回YES壶笼,則按照_key神僵、_isKey、key覆劈、isKey的順序查找成員變量保礼,找到直接賦值,找不到則拋出異常责语;
- 若accessInstanceVariablesDirectly方法返回NO炮障,則直接拋出異常;
- 取值過(guò)程
- 首先會(huì)按照getKey鹦筹、key铝阐、isKey、_key的順序查找方法铐拐,找到直接調(diào)用取值
- 若未找到徘键,則查看+ (BOOL)accessInstanceVariablesDirectly的返回值,若返回NO遍蟋,則直接拋出異常吹害;
- 若返回的YES,則按照_key虚青、_isKey它呀、key、isKey的順序查找成員變量,找到則取值纵穿;
- 找不到則拋出異常下隧;
使用場(chǎng)景
- 單層字典模型轉(zhuǎn)化:
[self.model setValuesForKeysWithDictionary:dict];
- 通過(guò)KVC修改未暴露的屬性:
UILabel *placeholderLabel=[self.userTextField valueForKeyPath:@"placeholderLabel"];
placeholderLabel.textColor = [UIColor redColor];
- 使用valueForKeyPath可以獲取數(shù)組中的最小值、最大值谓媒、平均值淆院、求和
CGFloat sum = [[array valueForKeyPath:@"@sum.floatValue"] floatValue];
CGFloat avg = [[array valueForKeyPath:@"@avg.floatValue"] floatValue];
CGFloat max =[[array valueForKeyPath:@"@max.floatValue"] floatValue];
CGFloat min =[[array valueForKeyPath:@"@min.floatValue"] floatValue];
- 數(shù)組內(nèi)部去重
[dataArray valueForKeyPath:@"@distinctUnionOfObjects.self"]
- 數(shù)組合并(去重合并:distinctUnionOfArrays.self、直接合并:unionOfArrays.self)
NSArray *temp1 = @[@3, @2, @2, @1];
NSArray *temp2 = @[@3, @4, @5];
NSLog(@"\n%@",[@[temp1, temp2] valueForKeyPath:@"@distinctUnionOfArrays.self"]);
NSLog(@"\n%@",[@[temp1, temp2] valueForKeyPath:@"@unionOfArrays.self"]);
輸出兩個(gè)數(shù)組:( 5, 1, 2, 3, 4 ), ( 3, 2, 2, 1, 3, 4, 5 )句惯。
- 大小寫(xiě)轉(zhuǎn)換(uppercaseString)及 打印字符串長(zhǎng)度同樣適用(length)
NSArray *array = @[@"name", @"w", @"aa", @"jimsa"];
NSLog(@"%@", [array valueForKeyPath:@"uppercaseString"]);
打油帘纭:
(NAME,W,AA,JIMSA)
九、Category
Category相關(guān)
Category的使用場(chǎng)合
- 將一個(gè)類(lèi)拆成很多模塊(其實(shí)就是解耦抢野,將相關(guān)的功能放到一起)
Category的實(shí)現(xiàn)原理
- 通過(guò)runtime動(dòng)態(tài)將分類(lèi)的方法合并到類(lèi)對(duì)象拷淘、元類(lèi)對(duì)象中
- Category編譯之后的底層結(jié)構(gòu)是 struct_category_t , 里面存儲(chǔ)著分類(lèi)的對(duì)象方法、類(lèi)方法指孤、屬性启涯、協(xié)議信息
- 在程序運(yùn)行的時(shí)候,runtime會(huì)將 Category 的數(shù)據(jù)恃轩,合并到類(lèi)信息中(類(lèi)對(duì)象逝嚎、元類(lèi)對(duì)象)
category和extension區(qū)別
- Extension在編譯時(shí),就把信息合并到類(lèi)信息中
- Category是在運(yùn)行時(shí)详恼,才會(huì)將分類(lèi)信息合并到類(lèi)信息中
- 分類(lèi)聲明的屬性补君,只會(huì)生成getter/setter方法聲明,不會(huì)自動(dòng)生成成員變量和getter/setter方法實(shí)現(xiàn)昧互,而擴(kuò)展會(huì)
- 分類(lèi)不可用為類(lèi)添加實(shí)例變量挽铁,而擴(kuò)展可以
分類(lèi)的局限性
- 無(wú)法為類(lèi)添加實(shí)例變量,但可通過(guò)關(guān)聯(lián)對(duì)象進(jìn)行實(shí)現(xiàn)
- 分類(lèi)的方法如果和類(lèi)重名敞掘,會(huì)覆蓋原來(lái)方法的實(shí)現(xiàn)
- 多個(gè)分類(lèi)的方法重名叽掘,會(huì)調(diào)用最后編譯的那個(gè)分類(lèi)的實(shí)現(xiàn)
為什么category不能添加屬性?使用Runtime就可以了玖雁?
- 分類(lèi)沒(méi)有自己的isa指針
- 類(lèi)最開(kāi)始生成了很多基本屬性更扁,比如IvarList,MethodList
- 分類(lèi)只會(huì)將自己的method attach到主類(lèi)赫冬,并不會(huì)影響到主類(lèi)的IvarList
- 實(shí)例變量沒(méi)有setter和getter方法浓镜。也沒(méi)有自己的isa指針
- 關(guān)聯(lián)對(duì)象都由AssociationsManager管理
- AssociationsManager里面是由一個(gè)靜態(tài)AssociationsHashMap來(lái)存儲(chǔ)所有的關(guān)聯(lián)對(duì)象的。
- 相當(dāng)于把所有對(duì)象的關(guān)聯(lián)對(duì)象都存在一個(gè)全局map里面劲厌。而map的的key是這個(gè)對(duì)象的指針地址
- 而這個(gè)map的value又是另外一個(gè)AssAssociationsHashMap膛薛,里面保存了關(guān)聯(lián)對(duì)象的kv對(duì)
Category中有l(wèi)oad方法么?load方法什么時(shí)候調(diào)用的补鼻?load方法能繼承么哄啄?
有
+load方法會(huì)在runtime加載類(lèi)雅任、分類(lèi)時(shí)調(diào)用;
每個(gè)類(lèi)咨跌、分類(lèi)的+load沪么,在程序運(yùn)行過(guò)程中只調(diào)用一次
調(diào)用順序
先調(diào)用類(lèi)的+load,(按照編譯先后順序锌半,先編譯成玫,先調(diào)用),調(diào)用子類(lèi)的+load之前會(huì)調(diào)用父類(lèi)的+load
再調(diào)用分類(lèi)的+load按照編譯先后順序調(diào)用(先編譯拳喻,先調(diào)用)
test方法和load方法的本質(zhì)區(qū)別?(+load方法為什么不會(huì)被覆蓋)
- test方法是通過(guò)消息機(jī)制調(diào)用 objc_msgSend([MJPerson class], @selector(test))
- +load方法調(diào)用猪腕,直接找到內(nèi)存中的地址冗澈,進(jìn)行方法調(diào)用
load調(diào)用順序
- +load方法會(huì)在runtime加載類(lèi)、分類(lèi)時(shí)調(diào)用
- 每個(gè)類(lèi)陋葡、分類(lèi)的+load亚亲,在程序運(yùn)行過(guò)程中只調(diào)用一次
- 調(diào)用順序
- 先調(diào)用類(lèi)的+load方法,之后按照編譯先后順序調(diào)用(先編譯腐缤,先調(diào)用捌归,調(diào)用子類(lèi)的+load之前會(huì)先調(diào)用父類(lèi)的+load)
- 再調(diào)用分類(lèi)的+load,之后按照編譯先后順序調(diào)用(先編譯岭粤,先調(diào)用)
不同Category中存在同一個(gè)方法惜索,會(huì)執(zhí)行哪個(gè)方法?如果是兩個(gè)都執(zhí)行剃浇,執(zhí)行順序是什么樣的巾兆?
- 根據(jù)Build Phases->Compile Sources中添加的文件順序,后面的會(huì)覆蓋前面的
load虎囚、initialize方法的區(qū)別是什么角塑?它們?cè)赾ategory中的調(diào)用順序?以及出現(xiàn)繼承時(shí)他們之間的調(diào)用過(guò)程淘讥???????????
區(qū)別:
- 調(diào)用方式不同
- load是根據(jù)函數(shù)地址直接調(diào)用
- initialize是通過(guò)objc_msgSend調(diào)用
- 調(diào)用時(shí)刻
- load是runtime加載 類(lèi)/分類(lèi) 的時(shí)候調(diào)用(只會(huì)調(diào)用1次)
- initialize是類(lèi)第一次接收消息時(shí)調(diào)用圃伶,每一個(gè)類(lèi)只會(huì)initialize一次(父類(lèi)的initialize方法可能會(huì)被調(diào)用多次)
- 調(diào)用順序
- load:先調(diào)用類(lèi)的load。先編譯的類(lèi)蒲列,優(yōu)先調(diào)用load(調(diào)用子類(lèi)的load之前窒朋,會(huì)先調(diào)用父類(lèi)的load)
- 再調(diào)用分類(lèi)的load(先編譯的分類(lèi),優(yōu)先調(diào)用load)
- initialize:先初始化父類(lèi)蝗岖, 再初始化子類(lèi)(可能最終調(diào)用的是父類(lèi)的initialize方法)
分類(lèi)中方法替換
- category的方法沒(méi)有“完全替換掉”原來(lái)類(lèi)已經(jīng)有的方法炼邀,也就是說(shuō)如果category和原來(lái)類(lèi)都有methodA,那么category附加完成之后剪侮,類(lèi)的方法列表里會(huì)有兩個(gè)methodA
- category的方法被放到了新方法列表的前面拭宁,而原來(lái)類(lèi)的方法被放到了新方法列表的后面
- 這也就是我們平常所說(shuō)的category的方法會(huì)“覆蓋”掉原來(lái)類(lèi)的同名方法洛退,這是因?yàn)檫\(yùn)行時(shí)在查找方法的時(shí)候是順著方法列表的順序查找的,它只要一找到對(duì)應(yīng)名字的方法杰标,就會(huì)罷休_兵怯,殊不知后面可能還有一樣名字的方法。
為什么不能動(dòng)態(tài)添加成員變量腔剂?
- 方法和屬性并不“屬于”類(lèi)實(shí)例媒区,而成員變量“屬于”類(lèi)實(shí)例
- “類(lèi)實(shí)例”概念,指的是一塊內(nèi)存區(qū)域掸犬,包含了isa指針和所有的成員變量袜漩。
- 假如允許動(dòng)態(tài)修改類(lèi)成員變量布局,已經(jīng)創(chuàng)建出的類(lèi)實(shí)例就不符合類(lèi)定義了湾碎,變成了無(wú)效對(duì)象宙攻。但方法定義是在objc_class中管理的,不管如何增刪類(lèi)方法介褥,都不影響類(lèi)實(shí)例的內(nèi)存布局座掘,已經(jīng)創(chuàng)建出的類(lèi)實(shí)例仍然可正常使用
十、網(wǎng)絡(luò)
TCP柔滔、UDP
TCP溢陪、UDP優(yōu)點(diǎn)及缺點(diǎn)
-
TCP優(yōu)點(diǎn):( 可靠,穩(wěn)定)
- 在傳遞數(shù)據(jù)之前睛廊,會(huì)有三次握手來(lái)建立連接形真,
- 在數(shù)據(jù)傳遞時(shí),有確認(rèn)超全、窗口没酣、重傳、擁塞控制機(jī)制卵迂,
- 在數(shù)據(jù)傳完后裕便,還會(huì)斷開(kāi)連接用來(lái)節(jié)約系統(tǒng)資源
-
TCP缺點(diǎn):(慢,效率低见咒,占用系統(tǒng)資源高)
- TCP在傳遞數(shù)據(jù)之前偿衰,要先建連接,這會(huì)消耗時(shí)間
- 在數(shù)據(jù)傳遞時(shí)(確認(rèn)機(jī)制改览、重傳機(jī)制下翎、擁塞控制機(jī)制)等都會(huì)消耗大量的時(shí)間
- 因?yàn)門(mén)CP有確認(rèn)機(jī)制、三次握手機(jī)制宝当,這些也導(dǎo)致TCP容易被人利用视事,實(shí)現(xiàn)DOS、DDOS庆揩、CC等攻擊
-
UDP的優(yōu)點(diǎn):(快)
- UDP沒(méi)有TCP的握手俐东、確認(rèn)跌穗、窗口、重傳虏辫、擁塞控制等機(jī)制
- UDP是一個(gè)無(wú)狀態(tài)的傳輸協(xié)議蚌吸,所以它在傳遞數(shù)據(jù)時(shí)非常快
-
UDP的缺點(diǎn):(不可靠砌庄,不穩(wěn)定)
- 因?yàn)閁DP沒(méi)有TCP那些可靠的機(jī)制羹唠,在數(shù)據(jù)傳遞時(shí),如果網(wǎng)絡(luò)質(zhì)量不好娄昆,就會(huì)很容易丟包
TCP佩微、UDP的區(qū)別
- TCP面向連接(如打電話要先撥號(hào)建立連接); UDP是無(wú)連接的,即發(fā)送數(shù)據(jù)
- TCP提供可靠的服務(wù)萌焰。通過(guò)TCP連接傳送的數(shù)據(jù)哺眯,無(wú)差錯(cuò),不丟失杆怕,不重復(fù),且按序到達(dá)壳贪;UDP盡最大努力交付陵珍,即不保證可靠交付
- TCP面向字節(jié)流,實(shí)際上是TCP把數(shù)據(jù)看成一連串無(wú)結(jié)構(gòu)的字節(jié)流; UDP是面向報(bào)文的
- 每一條TCP連接只能是點(diǎn)到點(diǎn)的; UDP支持一對(duì)一违施,一對(duì)多互纯,多對(duì)一和多對(duì)多的交互通信
Scoket連接和HTTP連接的區(qū)別
- HTTP協(xié)議是基于TCP連接的,是應(yīng)用層協(xié)議磕蒲,主要解決如何包裝數(shù)據(jù)留潦。Socket是對(duì)TCP/IP協(xié)議的封裝,Socket本身并不是協(xié)議辣往,而是一個(gè)調(diào)用接口(API)兔院,通過(guò)Socket,我們才能使用TCP/IP協(xié)議站削。
- HTTP連接:短連接坊萝,客戶端向服務(wù)器發(fā)送一次請(qǐng)求,服務(wù)器響應(yīng)后連接斷開(kāi)塑娇,節(jié)省資源碱璃。服務(wù)器不能主動(dòng)給客戶端響應(yīng)转捕,iPhone主要使用類(lèi)NSURLConnection
- Socket連接:長(zhǎng)連接,客戶端跟服務(wù)器端直接使用Socket進(jìn)行連接惦积,沒(méi)有規(guī)定連接后斷開(kāi),因此客戶端和服務(wù)器段保持連接通道猛频,雙方可以主動(dòng)發(fā)送數(shù)據(jù)
HTTP協(xié)議的特點(diǎn)狮崩,關(guān)于HTTP請(qǐng)求GET和POST的區(qū)別
- 特點(diǎn):
- HTTP超文本傳輸協(xié)議蛛勉,是短連接,是客戶端主動(dòng)發(fā)送請(qǐng)求厉亏,服務(wù)器做出響應(yīng)董习,服務(wù)器響應(yīng)之后,鏈接斷開(kāi)
- HTTP是一個(gè)屬于應(yīng)用層面向?qū)ο蟮膮f(xié)議爱只,HTTP有兩類(lèi)報(bào)文:請(qǐng)求報(bào)文和響應(yīng)報(bào)文
- HTTP請(qǐng)求報(bào)文:一個(gè)HTTP請(qǐng)求報(bào)文由請(qǐng)求行皿淋、請(qǐng)求頭部、空行和請(qǐng)求數(shù)據(jù)4部分組成
- HTTP響應(yīng)報(bào)文:由三部分組成:狀態(tài)行恬试、消息報(bào)頭窝趣、響應(yīng)正文
- GET請(qǐng)求
- 參數(shù)在地址后拼接,不安全(因?yàn)樗袇?shù)都拼接在地址后面)
- 不適合傳輸大量數(shù)據(jù)(長(zhǎng)度有限制训柴,為1024個(gè)字節(jié))
- POST請(qǐng)求
- 參數(shù)在請(qǐng)求數(shù)據(jù)區(qū)放著哑舒,相對(duì)GET請(qǐng)求更安全
- 數(shù)據(jù)大小理論上沒(méi)有限制
- 提交的數(shù)據(jù)放置在HTTP包的包體中
斷點(diǎn)續(xù)傳怎么實(shí)現(xiàn)的?
- 斷點(diǎn)續(xù)傳主要依賴于 HTTP 頭部定義的 Range 來(lái)完成
- 有了 Range幻馁,應(yīng)用可以通過(guò) HTTP 請(qǐng)求獲取失敗的資源洗鸵,從而來(lái)恢復(fù)下載該資源
- 當(dāng)然并不是所有的服務(wù)器都支持 Range,但大多數(shù)服務(wù)器是可以的仗嗦。Range 是以字節(jié)計(jì)算的膘滨,請(qǐng)求的時(shí)候不必給出結(jié)尾字節(jié)數(shù),因?yàn)檎?qǐng)求方并不一定知道資源的大小
TCP建立和斷開(kāi)連接過(guò)程
a稀拐、 三次握手
- 定義:
- 三次握手(Three-way Handshake)其實(shí)就是指建立一個(gè)TCP連接時(shí)火邓,需要客戶端和服務(wù)器總共發(fā)送3個(gè)包。進(jìn)行三次握手的主要作用就是為了確認(rèn)雙方的接收能力和發(fā)送能力是否正常德撬、指定自己的初始化序列號(hào)為后面的可靠性傳送做準(zhǔn)備铲咨。實(shí)質(zhì)上其實(shí)就是連接服務(wù)器指定端口,建立TCP連接蜓洪,并同步連接雙方的序列號(hào)和確認(rèn)號(hào)纤勒,交換TCP窗口大小信息。
- 第一次握手:客戶端給服務(wù)端發(fā)一個(gè) SYN 報(bào)文隆檀,并指明客戶端的初始化序列號(hào) ISN?踊东。此時(shí)客戶端處于 SYN_SEND 狀態(tài)。
首部的同步位SYN=1刚操,初始序號(hào)seq=x闸翅,SYN=1的報(bào)文段不能攜帶數(shù)據(jù),但要消耗掉一個(gè)序號(hào)菊霜。 - 第二次握手:服務(wù)器收到客戶端的 SYN 報(bào)文之后坚冀,會(huì)以自己的 SYN 報(bào)文作為應(yīng)答,并且也是指定了自己的初始化序列號(hào) ISN(s)鉴逞。同時(shí)會(huì)把客戶端的 ISN + 1 作為ACK 的值记某,表示自己已經(jīng)收到了客戶端的 SYN司训,此時(shí)服務(wù)器處于 SYN_REVD 的狀態(tài)。
在確認(rèn)報(bào)文段中SYN=1液南,ACK=1壳猜,確認(rèn)號(hào)ack=x+1,初始序號(hào)seq=y滑凉。 - 第三次握手:客戶端收到 SYN 報(bào)文之后统扳,會(huì)發(fā)送一個(gè) ACK 報(bào)文,當(dāng)然畅姊,也是一樣把服務(wù)器的 ISN + 1 作為 ACK 的值咒钟,表示已經(jīng)收到了服務(wù)端的 SYN 報(bào)文,此時(shí)客戶端處于 ESTABLISHED 狀態(tài)若未。服務(wù)器收到 ACK 報(bào)文之后朱嘴,也處于 ESTABLISHED 狀態(tài),此時(shí)粗合,雙方已建立起了連接萍嬉。
b、 三次握手為什么是三次握手
- 如果是兩次握手:
如客戶端發(fā)出連接請(qǐng)求隙疚,但因連接請(qǐng)求報(bào)文丟失而未收到確認(rèn)壤追,于是客戶端再重傳一次連接請(qǐng)求。后來(lái)收到了確認(rèn)甚淡,建立了連接大诸。數(shù)據(jù)傳輸完畢后捅厂,就釋放了連接贯卦,客戶端共發(fā)出了兩個(gè)連接請(qǐng)求報(bào)文段,其中第一個(gè)丟失焙贷,第二個(gè)到達(dá)了服務(wù)端撵割,但是第一個(gè)丟失的報(bào)文段只是在某些網(wǎng)絡(luò)結(jié)點(diǎn)長(zhǎng)時(shí)間滯留了,延誤到連接釋放以后的某個(gè)時(shí)間才到達(dá)服務(wù)端辙芍,此時(shí)服務(wù)端誤認(rèn)為客戶端又發(fā)出一次新的連接請(qǐng)求啡彬,于是就向客戶端發(fā)出確認(rèn)報(bào)文段,同意建立連接故硅,不采用三次握手庶灿,只要服務(wù)端發(fā)出確認(rèn),就建立新的連接了吃衅,此時(shí)客戶端忽略服務(wù)端發(fā)來(lái)的確認(rèn)往踢,也不發(fā)送數(shù)據(jù),則服務(wù)端一致等待客戶端發(fā)送數(shù)據(jù)徘层,浪費(fèi)資源峻呕。
c利职、 四次揮手
- 定義:
- 建立一個(gè)連接需要三次握手,而終止一個(gè)連接要經(jīng)過(guò)四次揮手(也有將四次揮手叫做四次握手的)瘦癌。這由TCP的半關(guān)閉(half-close)造成的猪贪。所謂的半關(guān)閉,其實(shí)就是TCP提供了連接的一端在結(jié)束它的發(fā)送后還能接收來(lái)自另一端數(shù)據(jù)的能力讯私。TCP 的連接的拆除需要發(fā)送四個(gè)包热押,因此稱(chēng)為四次揮手(Four-way handshake),客戶端或服務(wù)器均可主動(dòng)發(fā)起揮手動(dòng)作妄帘。
- 第一次:TCP客戶端發(fā)送一個(gè)FIN報(bào)文楞黄,用來(lái)關(guān)閉客戶到服務(wù)器的數(shù)據(jù)傳送。
- 第二次:服務(wù)器收到這個(gè)FIN報(bào)文抡驼,它發(fā)回一個(gè)ACK報(bào)文鬼廓,確認(rèn)序號(hào)為收到的序號(hào)加1。和SYN一樣致盟,一個(gè)FIN報(bào)文將占用一個(gè)序號(hào)碎税。
- 第三次:服務(wù)器關(guān)閉客戶端的連接,發(fā)送一個(gè)FIN給客戶端馏锡。
- 第四次:客戶端發(fā)回ACK報(bào)文確認(rèn)雷蹂,并將確認(rèn)序號(hào)設(shè)置為收到序號(hào)加1。
d杯道、揮手為什么需要四次匪煌?
- 因?yàn)楫?dāng)服務(wù)端收到客戶端的SYN連接請(qǐng)求報(bào)文后,可以直接發(fā)送SYN+ACK報(bào)文党巾。其中ACK報(bào)文是用來(lái)應(yīng)答的萎庭,SYN報(bào)文是用來(lái)同步的。但是關(guān)閉連接時(shí)齿拂,當(dāng)服務(wù)端收到FIN報(bào)文時(shí)驳规,很可能并不會(huì)立即關(guān)閉SOCKET,所以只能先回復(fù)一個(gè)ACK報(bào)文署海,告訴客戶端吗购,“你發(fā)的FIN報(bào)文我收到了”。只有等到我服務(wù)端所有的報(bào)文都發(fā)送完了砸狞,我才能發(fā)送FIN報(bào)文捻勉,因此不能一起發(fā)送。故需要四次揮手刀森。
e踱启、為什么TIME_WAIT狀態(tài)需要經(jīng)過(guò)2MSL(最大報(bào)文段生存時(shí)間)才能返回到CLOSE狀態(tài)?
- 雖然按道理,四個(gè)報(bào)文都發(fā)送完畢禽捆,我們可以直接進(jìn)入CLOSE狀態(tài)了笙什,但是我們必須假象網(wǎng)絡(luò)是不可靠的,有可以最后一個(gè)ACK丟失胚想。所以TIME_WAIT狀態(tài)就是用來(lái)重發(fā)可能丟失的ACK報(bào)文琐凭。在Client發(fā)送出最后的ACK回復(fù),但該ACK可能丟失浊服。Server如果沒(méi)有收到ACK统屈,將不斷重復(fù)發(fā)送FIN片段。所以Client不能立即關(guān)閉牙躺,它必須確認(rèn)Server接收到了該ACK愁憔。Client會(huì)在發(fā)送出ACK之后進(jìn)入到TIME_WAIT狀態(tài)。Client會(huì)設(shè)置一個(gè)計(jì)時(shí)器孽拷,等待2MSL的時(shí)間吨掌。如果在該時(shí)間內(nèi)再次收到FIN,那么Client會(huì)重發(fā)ACK并再次等待2MSL脓恕。所謂的2MSL是兩倍的MSL(Maximum Segment Lifetime)膜宋。MSL指一個(gè)片段在網(wǎng)絡(luò)中最大的存活時(shí)間,2MSL就是一個(gè)發(fā)送和一個(gè)回復(fù)所需的最大時(shí)間炼幔。如果直到2MSL秋茫,Client都沒(méi)有再次收到FIN,那么Client推斷ACK已經(jīng)被成功接收乃秀,則結(jié)束TCP連接肛著。
f、SYN的初始值(ISN initialization sequence number)是一個(gè)隨機(jī)值
- SYN:同步標(biāo)志 跺讯,同步序列編號(hào)(Synchronize Sequence Numbers)枢贿。該標(biāo)志僅在三次握手建立TCP連接時(shí)有效。它提示TCP連接的服務(wù)端檢查序列編號(hào)抬吟,該序列編號(hào)為T(mén)CP連接初始端(一般是客戶端)的初始序列編號(hào)萨咕。在這里统抬,可以把 TCP序列編號(hào)看作是一個(gè)范圍從0到4火本,294,967聪建,295的32位計(jì)數(shù)器钙畔。通過(guò)TCP連接交換的數(shù)據(jù)中每一個(gè)字節(jié)都經(jīng)過(guò)序列編號(hào)。在TCP報(bào)頭中的序列編號(hào)欄包括了TCP分段中第一個(gè)字節(jié)的序列編號(hào)
- 客戶端向服務(wù)器發(fā)送一個(gè)同步數(shù)據(jù)包請(qǐng)求建立連接金麸,該數(shù)據(jù)包中擎析,初始序列號(hào)(ISN)是客戶端隨機(jī)產(chǎn)生的一個(gè)值,確認(rèn)號(hào)是0;
- 服務(wù)器收到這個(gè)同步請(qǐng)求數(shù)據(jù)包后揍魂,會(huì)對(duì)客戶端進(jìn)行一個(gè)同步確認(rèn)桨醋。這個(gè)數(shù)據(jù)包中,序列號(hào)(ISN)是服務(wù)器隨機(jī)產(chǎn)生的一個(gè)值现斋,確認(rèn)號(hào)是客戶端的初始序列號(hào)+1喜最;
- 客戶端收到這個(gè)同步確認(rèn)數(shù)據(jù)包后,再對(duì)服務(wù)器進(jìn)行一個(gè)確認(rèn)庄蹋。該數(shù)據(jù)包中瞬内,序列號(hào)是上一個(gè)同步請(qǐng)求數(shù)據(jù)包中的確認(rèn)號(hào)值,確認(rèn)號(hào)是服務(wù)器的初始序列號(hào)+1限书。
- ISN代表什么虫蝶?意義何在?
- ISN倦西,發(fā)送方的字節(jié)數(shù)據(jù)編號(hào)的原點(diǎn)能真,讓對(duì)方生成一個(gè)合法的接收窗口。
- ISN是固定不變的嗎扰柠?
- 動(dòng)態(tài)隨機(jī)舟陆。
- ISN為何要?jiǎng)討B(tài)隨機(jī)?
- 增加安全性耻矮,為了避免被第三方猜測(cè)到秦躯,從而被第三方偽造的RST報(bào)文Reset。
TCP建立連接的三次握手中裆装,第二次握手發(fā)送的包會(huì)包含的標(biāo)記踱承,最正確的描述是?
網(wǎng)絡(luò)層相關(guān)
網(wǎng)絡(luò)七層協(xié)議
Charles原理
HTTPS的連接建立流程
TCP分片 和 IP分片
Cookie和Session
-
Cookie
- HTTP協(xié)議是無(wú)狀態(tài)的哨免,服務(wù)器中沒(méi)有保存客戶端的狀態(tài)茎活,客戶端必須每次帶上自己的狀態(tài)去請(qǐng)求服務(wù)器
基于HTTP這種特點(diǎn),就產(chǎn)生了cookie/session - cookie主要是用來(lái)記錄用戶狀態(tài)琢唾,區(qū)分用戶载荔,狀態(tài)保存在客戶端
- HTTP協(xié)議是無(wú)狀態(tài)的哨免,服務(wù)器中沒(méi)有保存客戶端的狀態(tài)茎活,客戶端必須每次帶上自己的狀態(tài)去請(qǐng)求服務(wù)器
-
Session
- Web應(yīng)用程序中還經(jīng)常使用Session來(lái)記錄客戶端狀態(tài)。Session是服務(wù)器端使用的一種記錄客戶端狀態(tài)的機(jī)制采桃,使用上比Cookie簡(jiǎn)單一些懒熙,相應(yīng)的也增加了服務(wù)器的存儲(chǔ)壓力。
-
區(qū)別
- cookie數(shù)據(jù)存放在客戶的瀏覽器上普办,session數(shù)據(jù)放在服務(wù)器上工扎。
- cookie相比session不是很安全,別人可以分析存放在本地的cookie并進(jìn)行cookie欺騙,考慮到安全應(yīng)當(dāng)使用session衔蹲。
- session會(huì)在一定時(shí)間內(nèi)保存在服務(wù)器上肢娘。當(dāng)訪問(wèn)增多,會(huì)比較占用你服務(wù)器的性能,考慮到減輕服務(wù)器性能方面,應(yīng)當(dāng)使用cookie橱健。
- 單個(gè)cookie保存的數(shù)據(jù)不能超過(guò)4K而钞,很多瀏覽器都限制一個(gè)站點(diǎn)最多保存20個(gè)cookie。而session存儲(chǔ)在服務(wù)端拘荡,可以無(wú)限量存儲(chǔ)
- 所以:將登錄信息等重要信息存放為session;其他信息如果需要保留笨忌,可以放在cookie中
DNS是什么?DNS解析過(guò)程
-
定義:
- 域名系統(tǒng)(Domain Name System俱病,DNS)
- 因特網(wǎng)上的主機(jī)官疲,可以使用多種方式標(biāo)識(shí):
一種標(biāo)識(shí)方法就是用它的主機(jī)名,比如·www.baidu.com亮隙、www.google.com途凫、gaia.cs.umass.edu等
另外一種方式,就是直接使用定長(zhǎng)的溢吻、有著清晰層次結(jié)構(gòu)的IP地址
-
區(qū)別:
- 主機(jī)名:方便人們記憶和接受维费,但長(zhǎng)度不一、沒(méi)有規(guī)律的字符串促王,路由器并不方便處理
- IP地址:路由器方便處理犀盟,不便于人們記憶
為了折衷這兩種方式,需要一種能進(jìn)行主機(jī)名到IP地址轉(zhuǎn)換的目錄服務(wù)蝇狼,就是 域名系統(tǒng)(Domain Name System阅畴,DNS)
-
作用:
- 將用戶提供的主機(jī)名解析為IP地址
-
DNS解析過(guò)程(以www.163.com為例:)
- 打開(kāi)瀏覽器,輸入一個(gè)域名(www.163.com)迅耘〖妫客戶端會(huì)發(fā)出一個(gè)DNS請(qǐng)求到本地DNS服務(wù)器(本地DNS服務(wù)器一般都是你的網(wǎng)絡(luò)接入服務(wù)器商提供,比如中國(guó)電信颤专,中國(guó)移動(dòng))
- 本地DNS服務(wù)器會(huì)首先查詢它的緩存記錄纽哥,如果緩存中有此條記錄,直接返回結(jié)果栖秕。如果沒(méi)有春塌,向DNS根服務(wù)器進(jìn)行查詢。
- 根DNS服務(wù)器沒(méi)有記錄具體的域名和IP地址的對(duì)應(yīng)關(guān)系簇捍,而是給出域服務(wù)器的地址只壳,告訴他可以到域服務(wù)器上去繼續(xù)查詢
- 本地DNS服務(wù)器繼續(xù)向域服務(wù)器發(fā)出請(qǐng)求,在這個(gè)例子中垦写,請(qǐng)求的對(duì)象是.com域服務(wù)器吕世。
- .com域服務(wù)器收到請(qǐng)求之后彰触,也不會(huì)直接返回域名和IP地址的對(duì)應(yīng)關(guān)系梯投,而是告訴本地DNS服務(wù)器,你的域名的解析服務(wù)器的地址。
- 最后分蓖,本地DNS服務(wù)器向域名的解析服務(wù)器發(fā)出請(qǐng)求尔艇,這時(shí)就能收到一個(gè)域名和IP地址對(duì)應(yīng)關(guān)系,
- 本地DNS服務(wù)器不僅要把IP地址返回給用戶電腦么鹤,還要把這個(gè)對(duì)應(yīng)關(guān)系保存在緩存中终娃,以備下次別的用戶查詢時(shí),可以直接返回結(jié)果蒸甜,加快網(wǎng)絡(luò)訪問(wèn)棠耕。
HTTP和HTTPS的區(qū)別?Https為什么更加安全柠新?
- 傳輸信息安全性不同
- http協(xié)議:是超文本傳輸協(xié)議窍荧,信息是明文傳輸。(如果攻擊者截取了Web瀏覽器和網(wǎng)站服務(wù)器之間的傳輸報(bào)文恨憎,就可以直接讀懂其中的信息)
- https協(xié)議:是具有安全性的ssl加密傳輸協(xié)議蕊退,為瀏覽器和服務(wù)器之間的通信加密,確保數(shù)據(jù)傳輸?shù)陌踩?/li>
- 連接方式不同
- http協(xié)議:http的連接很簡(jiǎn)單憔恳,是無(wú)狀態(tài)的瓤荔。
- https協(xié)議:是由SSL+HTTP協(xié)議構(gòu)建的可進(jìn)行加密傳輸、身份認(rèn)證的網(wǎng)絡(luò)協(xié)議钥组。
- 端口不同
- http協(xié)議:使用的端口是80输硝。
- https協(xié)議:使用的端口是443.
- 證書(shū)申請(qǐng)方式不同
- http協(xié)議:免費(fèi)申請(qǐng)。
- https協(xié)議:需要到ca申請(qǐng)證書(shū)程梦,一般免費(fèi)證書(shū)很少腔丧,需要交費(fèi)。
ipv6
- IPv4 是互聯(lián)網(wǎng)協(xié)議( Internet Protocol作烟, IP)的第四版愉粤,也是第一個(gè)被廣泛使用,目前運(yùn)用最多的互聯(lián)網(wǎng)技術(shù)協(xié)議拿撩。
IPv4 地址格式是這個(gè)樣子: 123.58.25.46 - IPv6 是 IPv4 的下一個(gè)版本 衣厘。 IPv6 地址長(zhǎng)度為 128 位,地址空間增加了 2128-232 個(gè)压恒,它在提高安全性方面相比前代有著較大的提升影暴。此外,身份認(rèn)證和隱私權(quán)也是 IPv6 的關(guān)鍵特性探赫。
IPv6 地址格式是這個(gè)樣子: 2001:da8:215:4009:250:56ff:fe97:40c7 型宙。
十一、UI
Storyboard/Xib和純代碼UI相比伦吠,有哪些優(yōu)缺點(diǎn)妆兑?
- storyboard/xib優(yōu)點(diǎn)
- 簡(jiǎn)單直接魂拦。直接通過(guò)拖拽和點(diǎn)選即可完成配置。
- 跳轉(zhuǎn)關(guān)系清楚
- 缺點(diǎn):
- 協(xié)作沖突(多人提交代碼)
- 很難做到頁(yè)面繼承和重用
- 不便于進(jìn)行模塊化管理
- 影響性能(多圖層渲染)
Auto Layout
Auto Layout基本原理
- 一個(gè)約束本質(zhì)上就是一個(gè)表示視圖布局關(guān)系的線性方程搁嗓。一個(gè)完整的約束方程式
控制器
描述一下 UIViewController 的生命周期
- 當(dāng) UIViewController 被創(chuàng)建并在屏幕上顯示芯勘,其生命周期方法執(zhí)行順序?yàn)椋?
- alloc:創(chuàng)建對(duì)象,分配空間腺逛;
- init (initWithNibName|initWithCoder) :初始化對(duì)象荷愕,初始化數(shù)據(jù);
- loadView: 完成一些關(guān)鍵 view 的初始化工作棍矛,加載 view漠魏;
- viewDidLoad:載入完成敛纲,可以進(jìn)行自定義數(shù)據(jù)以及動(dòng)態(tài)創(chuàng)建其他控件彤避;
- viewWillAppear:表示視圖即將出現(xiàn)在屏幕之前屡久;
- viewWillLayoutSubviews:將要對(duì)子視圖進(jìn)行調(diào)整;
- viewDidLayoutSubviews:完成對(duì)子視圖的調(diào)整慨绳;
- viewDidAppear:表示視圖已在屏幕上完成渲染掉冶;
- viewWillDisappear:表示視圖將要被移除,在程序執(zhí)行完但屏幕上視圖還未消失的時(shí)候執(zhí)行脐雪;
- viewDidDisappear:表示視圖已經(jīng)被移除厌小,在程序執(zhí)行完屏幕視圖也消失的時(shí)候執(zhí)行;
- dealloc :視圖被銷(xiāo)毀战秋,此處需要對(duì)在 init 和 viewDidLoad 中創(chuàng)建的對(duì)象進(jìn)行釋放璧亚;
- didReceiveMemoryWarning:內(nèi)存警告。
如果頁(yè)面 A 跳轉(zhuǎn)到 頁(yè)面 B脂信,A 的 viewDidDisappear 方法和 B 的 viewDidAppear 方法哪個(gè)先調(diào)用癣蟋?
- A -->viewWillDisappear
- B-->viewWillAppear
- A-->viewDidDisappear
- B-->viewDidAppear
離屏渲染,隱式動(dòng)畫(huà)和顯式動(dòng)畫(huà)相關(guān)
離屏渲染觸發(fā)條件
- 背景色狰闪、邊框疯搅、背景色+邊框,再加上圓角+裁剪埋泵,因?yàn)?contents = nil 沒(méi)有需要裁剪處理的內(nèi)容幔欧,所以不會(huì)造成離屏渲染。
- 一旦為contents設(shè)置了內(nèi)容丽声,無(wú)論是圖片礁蔗、繪制內(nèi)容、有圖像信息的子視圖等雁社,再加上圓角+裁剪浴井,就會(huì)觸發(fā)離屏渲染。
事件響應(yīng)過(guò)程(響應(yīng)鏈)
事件的傳遞 (尋找最合適的view的過(guò)程)
- 當(dāng)一個(gè)事件發(fā)生后霉撵,事件會(huì)從父控件傳給子控件 (UIApplication->UIWindow->UIView->initial view)
事件的響應(yīng)
- 首先看initial view能否處理這個(gè)事件磺浙,如果不能則會(huì)將事件傳遞給其上級(jí)視圖(inital view的superView)
- 如果上級(jí)視圖仍然無(wú)法處理則會(huì)繼續(xù)往上傳遞洪囤;一直傳遞到視圖控制器view controller,首先判斷視圖控制器的根視圖view是否能處理此事件
- 如果不能則接著判斷該視圖控制器能否處理此事件屠缭,如果還是不能則繼續(xù)向上傳遞
- 一直到window箍鼓,如果window還是不能處理此事件則繼續(xù)交給application處理崭参,如果最后application還是不能處理此事件則將其丟棄
事件順序
- 事件的傳遞是從上到下(父控件到子控件)
- 事件的響應(yīng)是從下到上(順著響應(yīng)者鏈條向上傳遞:子控件到父控件)
事件穿透
- 假設(shè)有一個(gè)黃色控件和白色控件呵曹,白色空間覆蓋在黃色控件上
- 點(diǎn)擊白色view想要黃色view來(lái)響應(yīng)該事件,就是所謂的穿透
- 方法一
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
CGPoint yellowPoint = [self convertPoint:point toView:_yellowView];
if ([_yellowView pointInside:yellowPoint withEvent:event]) {
return _yellowView;
}
return [super hitTest:point withEvent:event];
}
- 方法二
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
CGPoint yellowPoint =[_yellowView convertPoint:point fromView:self];
if ([_yellowView pointInside:yellowPoint withEvent:event]){
return NO;
} else {
return [super pointInside:point withEvent:event];
}
}
手勢(shì)識(shí)別
手勢(shì)識(shí)別的過(guò)程
這里主要說(shuō)的是關(guān)于runloop的概念點(diǎn)
- 當(dāng)
_UIApplicationHandleEventQueue()
識(shí)別了一個(gè)手勢(shì)時(shí)何暮,其首先會(huì)調(diào)用Cancel奄喂,將當(dāng)前的 touchesBegin/Move/End 系列回調(diào)打斷 - 隨后系統(tǒng)將對(duì)應(yīng)的 UIGestureRecognizer 標(biāo)記為待處理
- 蘋(píng)果注冊(cè)了一個(gè) Observer 監(jiān)測(cè) BeforeWaiting (Loop即將進(jìn)入休眠) 事件
- 這個(gè) Observer 的回調(diào)函數(shù)是
_UIGestureRecognizerUpdateObserver()
,其內(nèi)部會(huì)獲取所有剛被標(biāo)記為待處理的 GestureRecognizer海洼,并執(zhí)行GestureRecognizer 的回調(diào)跨新。 - 當(dāng)有 UIGestureRecognizer 的變化(創(chuàng)建/銷(xiāo)毀/狀態(tài)改變)時(shí),這個(gè)回調(diào)都會(huì)進(jìn)行相應(yīng)處理
十二坏逢、其他
數(shù)組和鏈表
數(shù)組和鏈表的區(qū)別
- 數(shù)組在內(nèi)存上給出了連續(xù)的空間
- 鏈表域帐,內(nèi)存地址上可以是不連續(xù)的,每個(gè)鏈表的節(jié)點(diǎn)包括原來(lái)的內(nèi)存和下一個(gè)節(jié)點(diǎn)的信息(單向的一個(gè),雙向鏈表的話,會(huì)有兩個(gè))
數(shù)組和鏈表的優(yōu)缺點(diǎn)
- 數(shù)組:
- 優(yōu)點(diǎn): 使用方便是整,查詢效率比鏈表高肖揣,內(nèi)存為一連續(xù)的區(qū)域
- 缺點(diǎn): 大小固定,不適合動(dòng)態(tài)存儲(chǔ)浮入,不方便動(dòng)態(tài)添加
- 鏈表:
- 優(yōu)點(diǎn): 可動(dòng)態(tài)添加刪除龙优,大小可變
- 缺點(diǎn): 只能通過(guò)順次指針訪問(wèn),查詢效率低
靜態(tài)庫(kù)事秀、動(dòng)態(tài)庫(kù)
什么是庫(kù)彤断?
- 共享代碼,實(shí)現(xiàn)代碼的復(fù)用易迹,一般分為靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)宰衙。
靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的區(qū)別
- 靜態(tài)庫(kù)(.a和.framework 樣式):
- 鏈接時(shí)完整的拷貝到可執(zhí)行文件,多次使用多次拷貝睹欲,造成冗余菩浙,使包變的更大
- 但是代碼裝載速度快,執(zhí)行速度略比動(dòng)態(tài)庫(kù)快
- 動(dòng)態(tài)庫(kù):(.dylib和.framework)
- 鏈接時(shí)不復(fù)制句伶,程序運(yùn)行時(shí)由系統(tǒng)加在到內(nèi)存中劲蜻,供系統(tǒng)調(diào)用,系統(tǒng)加在一次考余,多次使用先嬉,共用節(jié)省內(nèi)存。
為什么framework既是靜態(tài)又是動(dòng)態(tài)楚堤?
- 系統(tǒng)的framework是動(dòng)態(tài)的疫蔓,自己創(chuàng)建的是靜態(tài)的含懊。
.a 和 .framework 的區(qū)別是什么?
- .a 是單純的二進(jìn)制文件衅胀,需要 .h文件配合岔乔,不能直接使用
- .framework是二進(jìn)制文件+資源文件,可以直接使用滚躯。 .framework = .a + .h + sorrceFile(資源文件)
NSDictionary
NSDictionary底層實(shí)現(xiàn)原理
- 在OC中NSDictionary是使用hash表來(lái)實(shí)現(xiàn)key和value的映射和存儲(chǔ)的雏门。
hash表存儲(chǔ)過(guò)程簡(jiǎn)單介紹: - 根據(jù)key值計(jì)算出它的hash值h;
- 假設(shè)箱子的個(gè)數(shù)是n掸掏,那么鍵值對(duì)應(yīng)該放在第(h%n)個(gè)箱子中茁影。
- 如果該箱子中已經(jīng)有了鍵值對(duì),就是用開(kāi)放尋址法或者拉鏈法解決沖突丧凤。使用拉鏈法解決哈希沖突時(shí)募闲,每個(gè)箱子其實(shí)是一個(gè)鏈表,屬于同一個(gè)箱子的所有鍵值對(duì)都會(huì)排列在鏈表中愿待。
十三浩螺、Objective-C 類(lèi)和對(duì)象相關(guān)
語(yǔ)言基礎(chǔ)
Objective-C 與 C、C++ 之間的聯(lián)系和區(qū)別是什么仍侥?
- Objective-C 與 C++ 都是從 C 演化而來(lái)的面向?qū)ο笳Z(yǔ)言要出,兩者都兼容標(biāo)準(zhǔn) C 語(yǔ)言。Objective-C 與 C++ 的區(qū)別主要有以下幾點(diǎn):
Objective-C 是完全動(dòng)態(tài)的访圃,而 C++是部分動(dòng)態(tài)的厨幻;
Objective-C 不支持多重繼承, 而 C++ 支持,不過(guò) Objective-C 通過(guò) proxy(代理) 或 Category(類(lèi)別)可以更優(yōu)雅地實(shí)現(xiàn)這一特性腿时;
Objective-C 通過(guò)互相傳遞消息實(shí)現(xiàn)函數(shù)調(diào)用况脆,而 C++ 直接進(jìn)行函數(shù)調(diào)用;
Objective-C 采用 protocol 協(xié)議(非正式和正式)的形式來(lái)定義接口批糟,而 C++ 采用虛函數(shù)的形式來(lái)定義接口格了;
Objective-C 沒(méi)有 C++ 里有的構(gòu)造函數(shù)和析構(gòu)函數(shù), 其對(duì)應(yīng)物為 alloc-init/free。
如何理解 Objective-C 為動(dòng)態(tài)運(yùn)行時(shí)語(yǔ)言徽鼎?
- 主要是將數(shù)據(jù)類(lèi)型的確定由編譯時(shí)盛末,推遲到了運(yùn)行時(shí)。簡(jiǎn)單來(lái)說(shuō)否淤,運(yùn)行時(shí)機(jī)制使我們直到運(yùn)行時(shí)才去決定一個(gè)對(duì)象的類(lèi)別悄但,以及調(diào)用該類(lèi)別對(duì)象指定方法。
Objective-C 中是否支持垃圾回收機(jī)制石抡?
Objective-C 是支持垃圾回收機(jī)制的檐嚣,但是在 iOS 中不可用,iOS 開(kāi)發(fā)只支持手動(dòng)內(nèi)存管理和 ARC(Automatic Reference Counting)啰扛。
Objective-C 中堆和棧的區(qū)別是什么嚎京?
堆空間的內(nèi)存是動(dòng)態(tài)分配的嗡贺,一般用于存放 Objective-C 對(duì)象,并且需要手動(dòng)釋放內(nèi)存鞍帝,不及時(shí)回收容易產(chǎn)生內(nèi)存泄露诫睬,ARC 環(huán)境下 Objective-C 對(duì)象由編譯器管理,不需要手動(dòng)釋放帕涌;椛惴玻空間的內(nèi)存由系統(tǒng)自動(dòng)分配,一般存放非 Objective-C 對(duì)象的基本數(shù)據(jù)類(lèi)型宵膨,例如 int架谎、float 等炸宵,由系統(tǒng)編譯器管理辟躏,不需要手動(dòng)管理內(nèi)存。
類(lèi)土全、對(duì)象
OC的類(lèi)信息存放在哪里捎琐?
- 對(duì)象方法、屬性裹匙、成員變量瑞凑、協(xié)議信息,存放在class對(duì)象中
- 類(lèi)方法概页,存放在meta-class對(duì)象中
- 成員變量的具體值籽御,存放在instance對(duì)象中
類(lèi)與結(jié)構(gòu)體的區(qū)別
- 結(jié)構(gòu)體只能封裝數(shù)據(jù),而類(lèi)還可以封裝行為
- 賦值:結(jié)構(gòu)體是拷貝惰匙,對(duì)象之間是地址
- 結(jié)構(gòu)體變量分配在椉继停空間(如果是一個(gè)局部變量的情況下),而對(duì)象分配在堆空間
class项鬼、meta-class的結(jié)構(gòu)
struct objc_class : objc_object {
Class ISA;
Class superclass;
cache_t cache; // 方法緩存
class_data_bits_t bits; // 用于獲取具體的類(lèi)信息
}
& FAST_DATA_MASK
struct class_rw_t {
uint32_t flags;
uint32_t version;
const class_ro_t *ro; //
method_array_t methods; // 方法列表
property_array_t properties; // 屬性列表
protocol_array_t protocols; // 協(xié)議列表
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
}
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name; // 類(lèi)名
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars; // 成員變量列表
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
}
對(duì)象的isa指針指向哪里哑梳?superclass指針呢?
- instance的isa指向class
- class的isa指向meta-class
- meta-class的isa指向基類(lèi)的meta-class
- class的superclass指向父類(lèi)的class(如果沒(méi)有父類(lèi)绘盟,superclass指針為nil)
- meta-class的superclass指向父類(lèi)的meta-class
- 基類(lèi)的meta-class的superclass指向基類(lèi)的class
方法調(diào)用查找
- 對(duì)象方法的調(diào)用:通過(guò)instance的isa找到class鸠真,最后找到對(duì)象方法的實(shí)現(xiàn)進(jìn)行調(diào)用
- 類(lèi)方法的調(diào)用:當(dāng)調(diào)用類(lèi)方法時(shí),通過(guò)class的isa找到meta-class龄毡,最后找到類(lèi)方法的實(shí)現(xiàn)進(jìn)行調(diào)用
class對(duì)象的superclass指針
- Student : Person : NSObject
- 當(dāng)Student的instance對(duì)象要調(diào)用Personal的對(duì)象方法時(shí):
- 先通過(guò)isa找到Student的class吠卷,然后通過(guò)superclass找到Person的class,最后找到對(duì)象方法的實(shí)現(xiàn)進(jìn)行調(diào)用
meta-class對(duì)象的superclass指針
- 當(dāng)Student的class要調(diào)用Person的類(lèi)方法時(shí)
- 先通過(guò)isa找到Student的meta-class沦零,然后通過(guò)superclass找到Person的meta-class祭隔,最后找到類(lèi)方法的實(shí)現(xiàn)進(jìn)行調(diào)用
對(duì)象相關(guān)
屬性可以與set方法和get方法 三者同時(shí)存在嗎,如果不行,請(qǐng)說(shuō)明原因?
換句話說(shuō)就是:iOS中同時(shí)重寫(xiě)屬性的set與get方法時(shí),為什么訪問(wèn)不了下劃線屬性?
- 原因:
- 屬性的setter方法和getter方法是不能同時(shí)進(jìn)行重寫(xiě)蠢终,
- 因?yàn)樾蛉粒坏┠阃瑫r(shí)重寫(xiě)了這兩個(gè)方法茴她,那么系統(tǒng)就不會(huì)幫你生成這個(gè)成員變量了
- 解決方式:
@synthesize authType = _authType;
- 意思是,將屬性的setter,getter方法程奠,作用于這個(gè)變量丈牢。
iOS如何實(shí)現(xiàn)多繼承,代碼書(shū)寫(xiě)一下
- 使用協(xié)議組合
- NSProxy
- 比如我有兩個(gè)協(xié)議, 分別是 YFPerson,YFChild
#import <Foundation/Foundation.h>
@protocol YFPerson <NSObject>
@required
@property (nonatomic,copy,readonly)NSString *name;
- (NSInteger) age;
- (void)eat;
- (void)sleep;
@optional
- (void)play;
- (void)setName:(NSString *)newName;
@end
#import <Foundation/Foundation.h>
@protocol YFChild <NSObject>
@required
- (NSString *)nickname;
- (void)introduceMyselfWithName:(NSString *)name nickname:(NSString *)nickname age:(NSInteger)age;
@optional
- (void)study;
@end
- 那么, 我在新創(chuàng)建的一個(gè) YFStudent 類(lèi)中, 只要遵守上面兩個(gè)協(xié)議, 實(shí)現(xiàn)協(xié)議里的方法, 就可以在一個(gè)類(lèi)中,實(shí)現(xiàn)多個(gè)協(xié)議中的方法了.
- (NSString *)nickname
{
return @"龍兒";
}
- (NSInteger)age
{
return 19;
}
- (void)sleep{
NSLog(@"sleep");
}
- (void)eat{
NSLog(@"eat");
}
- (void)introduceMyselfWithName:(NSString *)name nickname:(NSString *)nickname age:(NSInteger)age
{
NSLog(@"我叫%@,小名%@,今天%@歲了", name,nickname,@(age));
}
- 這樣, 我在控制器的 viewDidLoad 方法中,創(chuàng)建 YFStudent 對(duì)象, 然后就可以調(diào)協(xié)議中的任何方法了
- (void)viewDidLoad {
[super viewDidLoad];
YFStudent *student = [[YFStudent alloc]init];
student.name = @"小龍女";
[student eat];
[student sleep];
[student introduceMyselfWithName:student.name nickname:student.nickname age:student.age];
}
關(guān)鍵字和系統(tǒng)提供的類(lèi)
簡(jiǎn)述__kindof關(guān)鍵字
- 表示當(dāng)前類(lèi)或者他的子類(lèi)(iOS9推出的瞄沙,一般用于消除警告)
- 聲明數(shù)組存貯指定UIView類(lèi)型的元素
- 如果元素被賦值為UIWebView或UIButton這樣的子類(lèi)型己沛,編譯器就會(huì)報(bào)警告
- 為了解決這個(gè)問(wèn)題,__kindof就應(yīng)運(yùn)而生距境。
@property (nonatomic, strong) NSMutableArray<UIView *> *viewList;
??
@property (nonatomic, strong) NSMutableArray <__kindof UIView *> * viewList;
新版Xcode這個(gè)問(wèn)題已經(jīng)優(yōu)化申尼,例子屬于老實(shí)例,理解這個(gè)意思就行了
關(guān)于NSProxy
- 消除NSTimer循環(huán)引用
- 多繼承
@interface LYBird ()
@property (nonatomic, copy) NSString *bridName;
@end
@implementation LYBird
- (void)onFly {
NSLog(@"%@正在飛翔", self.bridName);
}
@end
@interface LYFish ()
@property (nonatomic, copy) NSString *fishName;
@end
@implementation LYFish
- (void)onSwimming {
NSLog(@"%@正在游泳", self.fishName);
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
LYBird *bird = [[LYBird alloc] init];
LYFish *fish = [[LYFish alloc] init];
LYProxy *proxy = [LYProxy alloc];
[proxy transformToObject:bird];
[proxy performSelector:@selector(setBridName:) withObject:@"鷹隼"];
[proxy performSelector:@selector(onFly)];
[proxy transformToObject:fish];
[proxy performSelector:@selector(setFishName:) withObject:@"??"];
[proxy performSelector:@selector(onSwimming)];
}
id和NSObject ,instancetype的區(qū)別垫桂?
- id和instancetype都可以做方法的返回值师幕。
- id類(lèi)型的返回值在編譯期不能判斷對(duì)象的真實(shí)類(lèi)型,即非關(guān)聯(lián)返回類(lèi)型
- instancetype類(lèi)型的返回值在編譯期可以判斷對(duì)象的真實(shí)類(lèi)型诬滩,即關(guān)聯(lián)返回類(lèi)型霹粥。
- id可以用來(lái)定義變量, 可以作為返回值, 可以作為形參
- instancetype只能用于作為返回值。
nil疼鸟、Nil后控、NULL、NSNull的區(qū)別空镜?
- nil:指向一個(gè)對(duì)象的空指針
- Nil:指向一個(gè)類(lèi)的空指針,
- NULL:指向其他類(lèi)型(如:基本類(lèi)型浩淘、C類(lèi)型)的空指針, 用于對(duì)非對(duì)象指針賦空值.
- NSNull:在集合對(duì)象中,表示空值的對(duì)象.
NSNull在Objective-C中是一個(gè)類(lèi) .NSNull有 + (NSNull *)null; 單例方法.多用于集合(NSArray,NSDictionary)中值為空的對(duì)象.
NSArray *array = [NSArray arrayWithObjects: [[NSObject alloc] init], [NSNull null], @"aaa", nil, [[NSObject alloc] init], [[NSObject alloc] init], nil];
NSLog(@"%ld", array.count);// 輸出 3吴攒,NSArray以nil結(jié)尾
十四张抄、Swift
語(yǔ)言基礎(chǔ)
對(duì)比 Objective-C,Swift 有什么優(yōu)勢(shì)舶斧?
- 對(duì)比 Objective-C欣鳖,Swift 優(yōu)勢(shì)有如下幾點(diǎn):
- 語(yǔ)法簡(jiǎn)單易讀、代碼編寫(xiě)更加簡(jiǎn)潔茴厉、清晰泽台、易于維護(hù);
- 速度更快矾缓,運(yùn)算性能更高怀酷;
- 是一門(mén)類(lèi)型安全語(yǔ)言,代碼里值的類(lèi)型非常明確嗜闻;
- 泛型蜕依、結(jié)構(gòu)體、枚舉都很強(qiáng)大;
- 擁有便捷的函數(shù)式編程样眠。
在 Swift 中友瘤,為什么 map
函數(shù)必不可少?該在什么情況下使用它檐束?
- map 函數(shù)屬于高階函數(shù)辫秧,使用高階函數(shù)編程不僅可以優(yōu)化代碼,還能夠并行執(zhí)行被丧。map 函數(shù)通常用于數(shù)組中的每個(gè)元素盟戏,通過(guò)指定的方法進(jìn)行轉(zhuǎn)換,返回一個(gè)泛型的數(shù)組甥桂。
在 Swift 中使用擴(kuò)展可以完成什么任務(wù)柿究?
- Swift 中的擴(kuò)展可以動(dòng)態(tài)地給類(lèi)增加功能。在 Swift 中使用擴(kuò)展可以完成的任務(wù)有如下幾點(diǎn):
- 添加計(jì)算型屬性和計(jì)算靜態(tài)屬性黄选;
- 定義實(shí)例方法和類(lèi)型方法蝇摸;
- 提供新的構(gòu)造器;
- 定義下標(biāo)糕簿;
- 定義和使用新的嵌套類(lèi)型探入;
- 使一個(gè)已有類(lèi)型符合某個(gè)接口狡孔。
在 Swift 中懂诗,什么時(shí)候用結(jié)構(gòu)體,什么時(shí)候用類(lèi)苗膝?
- 在 Swift 開(kāi)發(fā)環(huán)境中殃恒,結(jié)構(gòu)體和類(lèi)都是構(gòu)建代碼所用的一種通用且靈活的構(gòu)造體,都擁有屬性辱揭,都可以定義方法离唐,但兩者存在很多不同的特性,類(lèi)是引用類(lèi)型问窃、支持繼承亥鬓,而結(jié)構(gòu)體是值類(lèi)型、不支持繼承域庇。此外嵌戈,由于結(jié)構(gòu)體的方法調(diào)用是靜態(tài)綁定,而類(lèi)的方法調(diào)用是動(dòng)態(tài)實(shí)現(xiàn)的听皿,所以在運(yùn)行時(shí)熟呛,結(jié)構(gòu)體的性能更優(yōu)。按照通用準(zhǔn)則尉姨,當(dāng)符合以下一條或多條情形時(shí)應(yīng)考慮創(chuàng)建一個(gè)結(jié)構(gòu)體:
- 結(jié)構(gòu)體的主要目的是為了封裝一些相關(guān)的簡(jiǎn)單數(shù)據(jù)值庵朝;
- 在賦予或者傳遞結(jié)構(gòu)實(shí)例時(shí),需要封裝的數(shù)據(jù)值被拷貝而不是引用;
- 在其他的情況下九府,定義一個(gè)類(lèi)椎瘟,并創(chuàng)建這個(gè)類(lèi)的實(shí)例,通過(guò)引用來(lái)管理和傳遞侄旬。
十五降传、App優(yōu)化等
系統(tǒng)架構(gòu)
iOS 系統(tǒng)可分為四級(jí)結(jié)構(gòu),由上至下分別為可觸摸層(Cocoa Touch Layer)勾怒、媒體層(Media Layer)婆排、核心服務(wù)層(Core Services Layer)、核心操作系統(tǒng)層(Core OS Layer)笔链,每個(gè)層級(jí)提供不同的服務(wù)段只。
低層級(jí)結(jié)構(gòu)提供基礎(chǔ)服務(wù)如文件系統(tǒng)、內(nèi)存管理鉴扫、I/O 操作等赞枕,高層級(jí)結(jié)構(gòu)建立在低層級(jí)結(jié)構(gòu)之上提供具體服務(wù)如 UI 控件、文件訪問(wèn)等坪创。
可觸摸層主要提供用戶交互相關(guān)的服務(wù)如界面控件炕婶、事件管理、通知中心莱预、地圖柠掂,包含的框架主要有 UIKit Framework、Event Kit UI Framework依沮、Notification Center Framework涯贞、Map Kit Framework等。
媒體層主要提供圖像引擎危喉、音頻引擎宋渔、視頻引擎框架。
核心服務(wù)層為程序提供基礎(chǔ)的系統(tǒng)服務(wù)例如網(wǎng)絡(luò)訪問(wèn)辜限、瀏覽器引擎皇拣、定位、文件訪問(wèn)薄嫡、數(shù)據(jù)庫(kù)訪問(wèn)等氧急,主要包含框架有 CFNetwork Framework、Core Data Framework岂座、Core Location Framework 和 Web Kit Framework 等态蒂。
核心系統(tǒng)層為上層結(jié)構(gòu)提供最基礎(chǔ)的服務(wù)如操作系統(tǒng)內(nèi)核服務(wù)、本地認(rèn)證费什、安全钾恢、加速等手素,主要包含框架有 Accelerate Framework、External Accessory Framework 和 Security Framework 等瘩蚪。
簡(jiǎn)述一下 iOS 的系統(tǒng)架構(gòu)
iOS 系統(tǒng)可分為四級(jí)結(jié)構(gòu)泉懦,由上至下分別為 可觸摸層(Cocoa Touch Layer)、媒體層(Media Layer)疹瘦、核心服務(wù)層(Core Services Layer)崩哩、核心操作系統(tǒng)層(Core OS Layer),每個(gè)層級(jí)提供不同的服務(wù)言沐〉肃冢可觸摸層主要提供用戶交互相關(guān)的服務(wù)如界面控件、事件管理险胰、通知中心汹押、地圖;媒體層主要提供圖像引擎起便、音頻引擎棚贾、視頻引擎框架;核心服務(wù)層為程序提供基礎(chǔ)的系統(tǒng)服務(wù)例如網(wǎng)絡(luò)訪問(wèn)榆综、瀏覽器引擎妙痹、定位、文件訪問(wèn)鼻疮、數(shù)據(jù)庫(kù)訪問(wèn)等怯伊;核心系統(tǒng)層為上層結(jié)構(gòu)提供最基礎(chǔ)的服務(wù)如操作系統(tǒng)內(nèi)核服務(wù)、本地認(rèn)證陋守、安全震贵、加速等。
Crash
Crash監(jiān)控
- NSSetUncaughtExceptionHandler可以統(tǒng)計(jì)閃退
App啟動(dòng)
- App啟動(dòng)分為兩種:
- 冷啟動(dòng)(Cold Launch):從零開(kāi)始啟動(dòng)app
- 熱啟動(dòng)(Warm Launch):app已在內(nèi)存中水评,在后臺(tái)存活,再次點(diǎn)擊圖標(biāo)啟動(dòng)app
- 啟動(dòng)時(shí)間的優(yōu)化媚送,主要是針對(duì)冷啟動(dòng)進(jìn)行優(yōu)化
- 通過(guò)添加環(huán)境變量可以打印app的啟動(dòng)時(shí)間分析
- DYLD_PRINT_STATISTICS
- DYLD_PRINT_STATISTICS_DETAILS(比上一個(gè)詳細(xì))
- 一般400毫秒以內(nèi)正常
打印結(jié)果:
Total pre-main time: 238.05 milliseconds (100.0%) // main函數(shù)調(diào)用之前(pre-main)總耗時(shí)
dylib loading time: 249.65 milliseconds (104.8%) // 動(dòng)態(tài)庫(kù)耗時(shí)
rebase/binding time: 126687488.8 seconds (18128259.6%)
ObjC setup time: 10.67 milliseconds (4.4%) // OC結(jié)構(gòu)體準(zhǔn)備耗時(shí)
initializer time: 52.83 milliseconds (22.1%) // 初始化耗時(shí)
slowest intializers : // 比較慢的加載
libSystem.B.dylib : 6.63 milliseconds (2.7%)
libBacktraceRecording.dylib : 6.61 milliseconds (2.7%)
libMainThreadChecker.dylib : 31.82 milliseconds (13.3%)
- 冷啟動(dòng)可以概括為3大階段
- dyld
- runtime
- main
- dyld(dynamic link editor)中燥,Apple的動(dòng)態(tài)連接器,可以裝載Mach-O(可執(zhí)行文件塘偎、動(dòng)態(tài)庫(kù)等)
- 裝載app的可執(zhí)行文件疗涉,同時(shí)遞歸加載所有依賴的動(dòng)態(tài)庫(kù)
- 當(dāng)dyld把可執(zhí)行文件、動(dòng)態(tài)庫(kù)都裝載完成后吟秩,會(huì)通知runtime進(jìn)行下一步處理
- runtime所做的事情
- 調(diào)用map_images函數(shù)中調(diào)用call_load_methods咱扣,調(diào)用所有Class和Category的+load方法
- 進(jìn)行各種objc結(jié)構(gòu)的初始化(注冊(cè)objc類(lèi)、初始化類(lèi)對(duì)象等等)
- 調(diào)用C++靜態(tài)初始化器和attribure((constructor))修飾的函數(shù)(JSONKit中存在具體應(yīng)用)
- 到此為止涵防,可執(zhí)行文件和動(dòng)態(tài)庫(kù)中所有的符號(hào)(Class, Protocol, Selector, IMP...)都已按格式成功加載到內(nèi)存中闹伪,被runtime所管理
- 總結(jié)
- app的啟動(dòng)由dylb主導(dǎo),將可執(zhí)行文件加載到內(nèi)存,順便加載所有依賴的動(dòng)態(tài)庫(kù)
- 并由runtime負(fù)責(zé)加載成objc定義的結(jié)構(gòu)
- 所有初始化工作結(jié)束后偏瓤,dyld就會(huì)調(diào)用main函數(shù)
- 接下來(lái)就是ApplicationMain函數(shù)杀怠,AppDelegate的application:didFinishLaunchingWithOptions:方法
項(xiàng)目的優(yōu)化、性能優(yōu)化
App啟動(dòng)優(yōu)化
- 依據(jù)App冷啟動(dòng)階段進(jìn)行優(yōu)化
- dyld
- 減少動(dòng)態(tài)庫(kù)厅克、合并一些動(dòng)態(tài)庫(kù)(定期清理不必要的動(dòng)態(tài)庫(kù))
- 減少objc類(lèi)赔退、分類(lèi)的數(shù)量、減少selector數(shù)量(定期清理不必要的類(lèi)证舟、分類(lèi))
- 減少C++虛構(gòu)函數(shù)
- Swift盡量使用struct
- runtime
- 使用+initialize方法和dispatch_once取代所有的attribute((constructor))硕旗、C++靜態(tài)構(gòu)造器、Objc的+load方法
- main
- 在不影響用戶體驗(yàn)的前提下女责,盡可能將一些操作延遲卵渴,不要全部都放在finishLaunching方法中
- 按需加載
App 包體積優(yōu)化
安裝包瘦身(ipa):資源文件、可執(zhí)行文件
-
資源文件(圖片鲤竹、音頻描滔、視頻等)
- 采取無(wú)損壓縮(使用工具)
- 去除沒(méi)有用到的資源(https://github.com/tinymind/LSUnusedResources)
-
可執(zhí)行文件瘦身:
- 編譯器優(yōu)化(Xcode相關(guān)配置)
- 利用AppCode(https://www.jetbrains.com/objc/) 檢測(cè)未使用的代碼:菜單欄 -> Code -> Inspect Code
- 生成LinkMap蚊俺,可以查看可執(zhí)行文件的具體組成
- 可借助第三方工具解析LinkMap文件:http://github.com/huanxsd/LinkMap
頁(yè)面瀏覽速度優(yōu)化
- json的處理(iOS 自帶的NSJSONSerialization,Jsonkit,SBJson)
- 數(shù)據(jù)的分頁(yè)(后端數(shù)據(jù)多的話涩哟,就要分頁(yè)返回,例如網(wǎng)易新聞驹马,或者 微博記錄)
- 數(shù)據(jù)壓縮(大數(shù)據(jù)也可以壓縮返回卷拘,減少流量,加快反應(yīng)速度)
- 內(nèi)容緩存(例如網(wǎng)易新聞的最新新聞列表都是要緩存到本地氮墨,從本地加載纺蛆,可以緩存到內(nèi)存,或者數(shù)據(jù)庫(kù)规揪,根據(jù)情況而定)
- 延時(shí)加載tab(比如app有5個(gè)tab桥氏,可以先加載第一個(gè)要顯示的tab,其他的在顯示時(shí)候加載猛铅,按需加載
- 算法的優(yōu)化(核心算法的優(yōu)化字支,例如有些app 有個(gè) 聯(lián)系人姓名用漢語(yǔ)拼音的首字母排序)
操作流暢度優(yōu)化
- Tableview 優(yōu)化(tableview cell的加載優(yōu)化)
- ViewController加載優(yōu)化(不同view之間的跳轉(zhuǎn),可以提前準(zhǔn)備好數(shù)據(jù))
數(shù)據(jù)持久化
SQLite
SQLite 數(shù)據(jù)庫(kù)的特點(diǎn)有哪些奸忽?
- SQLite 數(shù)據(jù)庫(kù)有以下特點(diǎn):
- 輕量級(jí)堕伪;
- 獨(dú)立性,沒(méi)有依賴栗菜,無(wú)需安裝和管理欠雌;
- 隔離性,全部在一個(gè)文件夾系統(tǒng)疙筹;
- 跨平臺(tái)性富俄, 支持眾多操作系統(tǒng)禁炒;
- 多語(yǔ)言接口,支持眾多編程語(yǔ)言蛙酪;
- SQLite 事務(wù)是完全兼容 ACID 的齐苛,通過(guò)獨(dú)占性和共享鎖來(lái)實(shí)現(xiàn)獨(dú)立事務(wù)的處理,允許從多個(gè)進(jìn)程或線程安全訪問(wèn)桂塞。
請(qǐng)簡(jiǎn)述幾種 SQLite 命令
- SQLite 的常用命令和其操作屬性分為以下幾種:
- CREATE : 創(chuàng)建一個(gè)新的表凹蜂,一個(gè)表的視圖,或者數(shù)據(jù)庫(kù)中的其他對(duì)象;
- ALTER: 修改數(shù)據(jù)庫(kù)中的某個(gè)已有的數(shù)據(jù)庫(kù)對(duì)象阁危,比如一個(gè)表玛痊;
- DROP: 刪除整個(gè)表,或者表的視圖狂打,或者數(shù)據(jù)庫(kù)中的其他對(duì)象擂煞;
- INSERT:創(chuàng)建一條記錄;
- UPDATE: 修改記錄趴乡;
- DELETE : 刪除記錄对省;
- SELECT:從一個(gè)或多個(gè)表中檢索某些記錄。
Core Data
Core Data 是什么晾捏?
- Core Data 是一個(gè)框架蒿涎,提供了對(duì)象—關(guān)系映射(ORM)的功能,既能夠?qū)?duì)象轉(zhuǎn)化成數(shù)據(jù)惦辛,保存在 SQLite 數(shù)據(jù)庫(kù)文件中劳秋,也可以按照需求將數(shù)據(jù)庫(kù)中的數(shù)據(jù)還原成對(duì)象。
Core Data 是一個(gè)關(guān)系型數(shù)據(jù)庫(kù)嗎胖齐?
- Core Data 不是一個(gè)關(guān)系型數(shù)據(jù)庫(kù)玻淑,也不是關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng),只是為數(shù)據(jù)變更管理呀伙、對(duì)象存儲(chǔ)补履、對(duì)象讀取恢復(fù)的功能提供了支持。
Core Data 與 SQLite 有無(wú)必然聯(lián)系区匠?
Core Data 與 SQLite 是有聯(lián)系的干像,Core Data 是基于 SQLite 數(shù)據(jù)庫(kù)的一個(gè)封裝,底層仍然是使用 SQLite 進(jìn)行存儲(chǔ)數(shù)據(jù)的驰弄。
對(duì)比分析 Core Data 和 SQLite 的優(yōu)缺點(diǎn)
- SQLite 是使用最多的 輕量級(jí)開(kāi)源數(shù)據(jù)庫(kù)引擎,無(wú)配置速客、無(wú)服務(wù)要求的事務(wù)數(shù)據(jù)庫(kù)引擎戚篙。其優(yōu)點(diǎn)有 獨(dú)立于服務(wù)器、零配置溺职、多進(jìn)程和線程下安全訪問(wèn)等岔擂。Core Data 更加關(guān)注于對(duì)象而不是傳統(tǒng)的表數(shù)據(jù)庫(kù)方法位喂,相比于 SQLite,CoreData 集成化乱灵,數(shù)據(jù)動(dòng)態(tài)加載塑崖,在取數(shù)據(jù)方面更快,不需要書(shū)寫(xiě)大量的 sql 語(yǔ)句痛倚,且還具備其他 sql 所不具備的優(yōu)點(diǎn)规婆,比如對(duì) undo 的支持、多個(gè) context 實(shí)現(xiàn)sketchbook 類(lèi)似的功能等蝉稳,但是 Core Data 數(shù)據(jù)模型升級(jí)兼容性較差 較差抒蚜,不便于處理多對(duì)多的關(guān)系,自定義升級(jí)麻煩耘戚,并且效率低下嗡髓。
測(cè)試
單元測(cè)試
- 測(cè)試可以分為黑盒測(cè)試和白盒測(cè)試,單元測(cè)試可以算是一種白盒測(cè)試的類(lèi)型收津,其目的是提高軟件開(kāi)發(fā)的效率饿这,維持代碼的健康性,助力開(kāi)發(fā)人員開(kāi)發(fā)健壯撞秋、安全的應(yīng)用程序长捧。
應(yīng)用程序的開(kāi)發(fā)者對(duì)現(xiàn)有的模塊編寫(xiě)相應(yīng)的測(cè)試代碼進(jìn)行測(cè)試,包含測(cè)試用例的設(shè)計(jì)部服。
又程序的開(kāi)發(fā)者自己編寫(xiě)測(cè)試代碼并進(jìn)行白盒測(cè)試唆姐,之后再交給測(cè)試團(tuán)隊(duì)進(jìn)行相應(yīng)的黑盒測(cè)試,這種方式有利于提升測(cè)試流程的完整性廓八,從而保證應(yīng)用程序的質(zhì)量奉芦。
單元測(cè)試比較適合進(jìn)行應(yīng)用程序的業(yè)務(wù)邏輯和網(wǎng)絡(luò)請(qǐng)求接口方面的測(cè)試。
單元測(cè)試的關(guān)鍵組件是測(cè)試用例剧蹂,測(cè)試用例可以在最低可測(cè)試的單元對(duì)代碼進(jìn)行測(cè)試声功。
什么是單元測(cè)試?
單元測(cè)試是針對(duì)程序的最小單元進(jìn)行正確性檢驗(yàn)的測(cè)試工作宠叼。
本文參考文章及部分內(nèi)容來(lái)源
作者:力扣 (LeetCode)
鏈接:https://leetcode-cn.com/leetbook/read/ios-interview/r1rxtj/
作者:強(qiáng)子ly
鏈接:http://www.reibang.com/p/24a9447d70f8