UIKit
1.UIView 和 CALayer 是什么關(guān)系踩衩?
UIView 繼承 UIResponder汁胆,而 UIResponder 是響應(yīng)者對象勤婚,可以對iOS 中的事件響應(yīng)及傳遞,CALayer 沒有繼承自 UIResponder勃痴,所以 CALayer 不具備響應(yīng)處理事件的能力。CALayer 是 QuartzCore 中的類热康,是一個比較底層的用來繪制內(nèi)容的類沛申,用來繪制UI
UIView 對 CALayer 封裝屬性,對 UIView 設(shè)置 frame姐军、center铁材、bounds 等位置信息時尖淘,其實(shí)都是UIView 對 CALayer 進(jìn)一層封裝,使得我們可以很方便地設(shè)置控件的位置著觉;例如圓角村生、陰影等屬性, UIView 就沒有進(jìn)一步封裝饼丘,所以我們還是需要去設(shè)置 Layer 的屬性來實(shí)現(xiàn)功能趁桃。
UIView 是 CALayer 的代理,UIView 持有一個 CALayer 的屬性肄鸽,并且是該屬性的代理镇辉,用來提供一些 CALayer 行的數(shù)據(jù),例如動畫和繪制贴捡。
2.Bounds 和 Frame 的區(qū)別?
Bounds:一般是相對于自身來說的忽肛,是控件的內(nèi)部尺寸。如果你修改了 Bounds烂斋,那么子控件的相對位置也會發(fā)生改變屹逛。
Frame :是相對于父控件來說的,是控件的外部尺寸汛骂。
3.setNeedsDisplay 和 layoutIfNeeded 兩者是什么關(guān)系罕模?[圖片上傳失敗...(image-fe66cb-1646208151160)]
UIView的setNeedsDisplay和setNeedsLayout兩個方法都是異步執(zhí)行的。而setNeedsDisplay會自動調(diào)用drawRect方法帘瞭,這樣可以拿到UIGraphicsGetCurrentContext進(jìn)行繪制淑掌;而setNeedsLayout會默認(rèn)調(diào)用layoutSubViews,給當(dāng)前的視圖做了標(biāo)記蝶念;layoutIfNeeded 查找是否有標(biāo)記抛腕,如果有標(biāo)記及立刻刷新。
只有setNeedsLayout和layoutIfNeeded這二者合起來使用媒殉,才會起到立刻刷新的效果担敌。
4.談?wù)剬IResponder的理解
UIResponder類是專門用來響應(yīng)用戶的操作處理各種事件的,包括觸摸事件(Touch Events)廷蓉、運(yùn)動事件(Motion Events)全封、遠(yuǎn)程控制事件(Remote Control Events)。我們知道UIApplication桃犬、UIView刹悴、UIViewController這幾個類是直接繼承自UIResponder,所以這些類都可以響應(yīng)事件攒暇。當(dāng)然我們自定義的繼承自UIView的View以及自定義的繼承自UIViewController的控制器都可以響應(yīng)事件土匀。
5.loadView的作用?
loadView方法會在每次訪問UIViewController的view(比如controller.view扯饶、self.view)而且view為nil時會被調(diào)用恒削,此方法主要用來負(fù)責(zé)創(chuàng)建UIViewController的view(重寫loadView方法,并且不需要調(diào)用[super loadView])
這里要提一下 [super loadView]尾序,[super loadView]做了下面幾件事钓丰。
它會先去查找與UIViewController相關(guān)聯(lián)的xib文件,通過加載xib文件來創(chuàng)建UIViewController的view每币,如果在初始化UIViewController指定了xib文件名携丁,就會根據(jù)傳入的xib文件名加載對應(yīng)的xib文件,如果沒有明顯地傳xib文件名兰怠,就會加載跟UIViewController同名的xib文件
如果沒有找到相關(guān)聯(lián)的xib文件梦鉴,就會創(chuàng)建一個空白的UIView,然后賦值給UIViewController的view屬性
綜上揭保,在需要自定義UIViewController的view時肥橙,可以通過重寫loadView方法且不需要調(diào)用[super loadView]方法。
6.使用 drawRect有什么影響秸侣?
drawRect 方法依賴 Core Graphics 框架來進(jìn)行自定義的繪制 缺點(diǎn):它處理 touch 事件時每次按鈕被點(diǎn)擊后存筏,都會用 setNeddsDisplay 進(jìn)行強(qiáng)制重繪;而且不止一次味榛,每次單點(diǎn)事件觸發(fā)兩次執(zhí)行椭坚。這樣的話從性能的角度來說,對 CPU 和內(nèi)存來說都是欠佳的搏色。特別是如果在我們的界面上有多個這樣的UIButton 實(shí)例善茎,那就會很糟糕了。這個方法的調(diào)用機(jī)制也是非常特別. 當(dāng)你調(diào)用 setNeedsDisplay 方法時, UIKit 將會把當(dāng)前圖層標(biāo)記為 dirty,但還是會顯示原來的內(nèi)容,直到下一次的視圖渲染周期,才會將標(biāo)記為 dirty 的圖層重新建立 Core Graphics 上下文,然后將內(nèi)存中的數(shù)據(jù)恢復(fù)出來, 再使用 CGContextRef 進(jìn)行繪制
"精品簡歷模板+全網(wǎng)BAT面試題+iOS提升視頻"網(wǎng)址 : docs.qq.com/doc/DVWlQam9Qd3B1cEF2
展示部分截圖:
[圖片上傳失敗...(image-4ba380-1679319743060)]
7.keyWindow 和 delegate的window有何區(qū)別
delegate.window 程序啟動時設(shè)置的window對象频轿。
keyWindow 這個屬性保存了[windows]數(shù)組中的[UIWindow]對象垂涯,該對象最近被發(fā)送了[makeKeyAndVisible]消息
一般情況下 delegate.window 和 keyWindow 是同一個對象,但不能保證keyWindow就是delegate.window航邢,因?yàn)閗eyWindow會因?yàn)閙akeKeyAndVisible而變化集币,例如,程序中添加了一個懸浮窗口翠忠,這個時候keywindow就會變化鞠苟。
Foundation
1.nil、NIL秽之、NSNULL 有什么區(qū)別当娱?
nil、NIL 可以說是等價的考榨,都代表內(nèi)存中一塊空地址跨细。
NSNULL 代表一個指向 nil 的對象。
2.如何實(shí)現(xiàn)一個線程安全的 NSMutableArray?
NSMutableArray是線程不安全的河质,當(dāng)有多個線程同時對數(shù)組進(jìn)行操作的時候可能導(dǎo)致崩潰或數(shù)據(jù)錯誤
線程鎖:使用線程鎖對數(shù)組讀寫時進(jìn)行加鎖
派發(fā)隊列:在《Effective Objective-C 2.0..》書中第41條:多用派發(fā)隊列冀惭,少用同步鎖中指出:使用“串行同步隊列”(serial synchronization queue)震叙,將讀取操作及寫入操作都安排在同一個隊列里,即可保證數(shù)據(jù)同步散休。而通過并發(fā)隊列媒楼,結(jié)合GCD的柵欄塊(barrier)來不僅實(shí)現(xiàn)數(shù)據(jù)同步線程安全,還比串行同步隊列方式更高效戚丸。
3.atomic 修飾的屬性是絕對安全的嗎划址?為什么?
不是限府,所謂的安全只是局限于 Setter夺颤、Getter 的訪問器方法而言的,你對它做 Release 的操作是不會受影響的胁勺。這個時候就容易崩潰了世澜。
4.實(shí)現(xiàn) isEqual 和 hash 方法時要注意什么?
-
hash
對關(guān)鍵屬性的hash值進(jìn)行位或運(yùn)算作為hash值
-
isEqual
==運(yùn)算符判斷是否是同一對象, 因?yàn)橥粚ο蟊厝煌耆嗤?/p>
判斷是否是同一類型, 這樣不僅可以提高判等的效率, 還可以避免隱式類型轉(zhuǎn)換帶來的潛在風(fēng)險
判斷對象是否是nil, 做參數(shù)有效性檢查
各個屬性分別使用默認(rèn)判等方法進(jìn)行判斷
返回所有屬性判等的與結(jié)果
5.id 和 instanceType 有什么區(qū)別署穗?
-
相同點(diǎn)
instancetype 和 id 都是萬能指針宜狐,指向?qū)ο蟆?/p>
-
不同點(diǎn):
1.id 在編譯的時候不能判斷對象的真實(shí)類型,instancetype 在編譯的時候可以判斷對象的真實(shí)類型蛇捌。
2.id 可以用來定義變量抚恒,可以作為返回值類型,可以作為形參類型络拌;instancetype 只能作為返回值類型俭驮。
6.self和super的區(qū)別
self調(diào)用自己方法,super調(diào)用父類方法
self是類春贸,super是預(yù)編譯指令
[self class] 和 [super class] 輸出是一樣的
-
self和super底層實(shí)現(xiàn)原理
1.當(dāng)使用 self 調(diào)用方法時混萝,會從當(dāng)前類的方法列表中開始找,如果沒有萍恕,就從父類中再找逸嘀;
而當(dāng)使用 super 時,則從父類的方法列表中開始找允粤,然后調(diào)用父類的這個方法
2.當(dāng)使用 self 調(diào)用時崭倘,會使用 objc_msgSend 函數(shù):
id objc_msgSend(id theReceiver, SEL theSelector, ...)
第一個參數(shù)是消息接收者,第二個參數(shù)是調(diào)用的具體類方法的 selector类垫,后面是 selector 方法的可變參數(shù)司光。以 [self setName:] 為例,編譯器會替換成調(diào)用 objc_msgSend 的函數(shù)調(diào)用悉患,其中 theReceiver 是 self残家,theSelector 是 @selector(setName:),這個 selector 是從當(dāng)前 self 的 class 的方法列表開始找的 setName售躁,當(dāng)找到后把對應(yīng)的 selector 傳遞過去坞淮。
3.當(dāng)使用 super 調(diào)用時茴晋,會使用 objc_msgSendSuper 函數(shù):
id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
第一個參數(shù)是個objc_super的結(jié)構(gòu)體,第二個參數(shù)還是類似上面的類方法的selector
struct objc_super { id receiver; Class superClass; };
7.@synthesize和@dynamic分別有什么作用回窘?
@property有兩個對應(yīng)的詞诺擅,一個是 @synthesize,一個是 @dynamic毫玖。如果 @synthesize和 @dynamic都沒寫,那么默認(rèn)的就是@syntheszie var = _var;
@synthesize 的語義是如果你沒有手動實(shí)現(xiàn) setter 方法和 getter 方法凌盯,那么編譯器會自動為你加上這兩個方法付枫。
@dynamic 告訴編譯器:屬性的 setter 與 getter 方法由用戶自己實(shí)現(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)綁定。
8.typeof 和 __typeof逼友,typeof 的區(qū)別?
__typeof __() 和 __typeof() 是 C語言 的編譯器特定擴(kuò)展精肃,因?yàn)闃?biāo)準(zhǔn) C 不包含這樣的運(yùn)算符。 標(biāo)準(zhǔn) C 要求編譯器用雙下劃線前綴語言擴(kuò)展(這也是為什么你不應(yīng)該為自己的函數(shù)帜乞,變量等做這些)
typeof() 與前兩者完全相同的司抱,只不過去掉了下劃線,同時現(xiàn)代的編譯器也可以理解黎烈。
所以這三個意思是相同的习柠,但沒有一個是標(biāo)準(zhǔn)C,不同的編譯器會按需選擇符合標(biāo)準(zhǔn)的寫法照棋。
9.類族
系統(tǒng)框架中有許多類簇津畸,大部分collection類都是類族。例如NSArray與其可變版本NSMutableArray必怜。這樣看來實(shí)際上有兩個抽象基類肉拓,一個用于不可變數(shù)組,一個用于可變數(shù)組梳庆。盡管具備公共接口的類有兩個暖途,但任然可以合起來算一個類族杯缺。不可變的類定義了對所有數(shù)組都通用的方法,而可變類則定義了那些只適用于可變數(shù)組的方法侧但。兩個類共同屬于同一個類族看政,這意味著二者在實(shí)現(xiàn)各自類型的數(shù)組時可以共用實(shí)現(xiàn)代碼,此外還能把可變數(shù)組復(fù)制成不可變數(shù)組欺栗,反之亦然毫痕。
10.struct和class的區(qū)別
類: 引用類型(位于棧上面的指針(引用)和位于堆上的實(shí)體對象)
結(jié)構(gòu):值類型(實(shí)例直接位于棧中)
WebView
1.說一下 JS 和 OC 互相調(diào)用的幾種方式?
-
js調(diào)用oc的三種方式:
根據(jù)網(wǎng)頁重定向截取字符串通過url scheme判斷
替換方法.context[@"copyText"]
注入對象:遵守協(xié)議JSExport,設(shè)置context[@
-
oc調(diào)用js代碼兩種方式
通過webVIew調(diào)用 webView stringByEvaluatingJavaScriptFromString: 調(diào)用
通過JSContext調(diào)用[context evaluateScript:];
2.在使用 WKWedView 時遇到過哪些問題迟几?
白屏問題消请,Cookie 問題,在WKWebView上直接使用NSURLProtocol無法攔截請求类腮,在WKWebView 上通過loadRequ發(fā)起的post請求body數(shù)據(jù)被丟失臊泰,截屏問題等
內(nèi)存管理
1.什么情況使用weak關(guān)鍵字,相比assign有什么不同蚜枢?
-
什么情況使用 weak 關(guān)鍵字缸逃?
在 ARC 中,在有可能出現(xiàn)循環(huán)引用的時候,往往要通過讓其中一端使用 weak 來解決,比如: delegate 代理屬性
自身已經(jīng)對它進(jìn)行一次強(qiáng)引用,沒有必要再強(qiáng)引用一次,此時也會使用 weak,自定義 IBOutlet 控件屬性一般也使用 weak;當(dāng)然厂抽,也可以使用strong需频。
-
不同點(diǎn):
weak 此特質(zhì)表明該屬性定義了一種“非擁有關(guān)系” (nonowning relationship)。為這種屬性設(shè)置新值時筷凤,設(shè)置方法既不保留新值贺辰,也不釋放舊值。此特質(zhì)同assign類似嵌施, 然而在屬性所指的對象遭到摧毀時饲化,屬性值也會清空(nil out)。 而 assign 的“設(shè)置方法”只會執(zhí)行針對“純量類型” (scalar type吗伤,例如 CGFloat 或 NSlnteger 等)的簡單賦值操作吃靠。
assign 可以用非 OC 對象,而 weak 必須用于 OC 對象
2.如何讓自己的類用copy修飾符?如何重寫帶copy關(guān)鍵字的setter足淆?[圖片上傳失敗...(image-93a42-1646208151160)]
-
若想令自己所寫的對象具有拷貝功能巢块,則需實(shí)現(xiàn) NSCopying 協(xié)議。如果自定義的對象分為可變版本與不可變版本巧号,那么就要同時實(shí)現(xiàn) NSCopying 與 NSMutableCopying 協(xié)議族奢。
具體步驟:
需聲明該類遵從 NSCopying 協(xié)議
實(shí)現(xiàn) NSCopying 協(xié)議。該協(xié)議只有一個方法:
- (id)copyWithZone:(NSZone *)zone;
注意:一提到讓自己的類用 copy 修飾符丹鸿,我們總是想覆寫copy方法越走,其實(shí)真正需要實(shí)現(xiàn)的卻是 “copyWithZone” 方法。
-
重寫帶 copy 關(guān)鍵字的 setter,例如:
- (void)setName:(NSString *)name { //[_name release]; _name = [name copy]; }
3.深拷貝與淺拷貝
淺拷貝只是對指針的拷貝廊敌,拷貝后兩個指針指向同一個內(nèi)存空間铜跑,深拷貝不但對指針進(jìn)行拷貝,而且對指針指向的內(nèi)容進(jìn)行拷貝骡澈,經(jīng)深拷貝后的指針是指向兩個不同地址的指針锅纺。
當(dāng)對象中存在指針成員時,除了在復(fù)制對象時需要考慮自定義拷貝構(gòu)造函數(shù)肋殴,還應(yīng)該考慮以下兩種情形:
當(dāng)函數(shù)的參數(shù)為對象時囤锉,實(shí)參傳遞給形參的實(shí)際上是實(shí)參的一個拷貝對象,系統(tǒng)自動通過拷貝構(gòu)造函數(shù)實(shí)現(xiàn)护锤;
當(dāng)函數(shù)的返回值為一個對象時官地,該對象實(shí)際上是函數(shù)內(nèi)對象的一個拷貝,用于返回函數(shù)調(diào)用處蔽豺。
copy方法:如果是非可擴(kuò)展類對象区丑,則是淺拷貝拧粪。如果是可擴(kuò)展類對象修陡,則是深拷貝。
mutableCopy方法:無論是可擴(kuò)展類對象還是不可擴(kuò)展類對象可霎,都是深拷貝魄鸦。
4.@property的本質(zhì)是什么?ivar癣朗、getter拾因、setter是如何生成并添加到這個類中的?[圖片上傳失敗...(image-987b75-1646208151159)]
-
@property 的本質(zhì)是實(shí)例變量(ivar)+存取方法(access method = getter + setter),即 @property = ivar + getter + setter;
“屬性” (property)作為 Objective-C 的一項(xiàng)特性旷余,主要的作用就在于封裝對象中的數(shù)據(jù)绢记。 Objective-C 對象通常會把其所需要的數(shù)據(jù)保存為各種實(shí)例變量。實(shí)例變量一般通過“存取方法”(access method)來訪問正卧。其中蠢熄,“獲取方法” (getter)用于讀取變量值,而“設(shè)置方法” (setter)用于寫入變量值炉旷。
-
ivar签孔、getter、setter 是自動合成這個類中的
完成屬性定義后窘行,編譯器會自動編寫訪問這些屬性所需的方法饥追,此過程叫做“自動合成”(autosynthesis)。需要強(qiáng)調(diào)的是罐盔,這個過程由編譯 器在編譯期執(zhí)行但绕,所以編輯器里看不到這些“合成方法”(synthesized method)的源代碼。除了生成方法代碼 getter惶看、setter 之外壁熄,編譯器還要自動向類中添加適當(dāng)類型的實(shí)例變量帚豪,并且在屬性名前面加下劃線,以此作為實(shí)例變量的名字草丧。在前例中狸臣,會生成兩個實(shí)例變量,其名稱分別為 _firstName 與 _lastName昌执。也可以在類的實(shí)現(xiàn)代碼里通過 @synthesize 語法來指定實(shí)例變量的名字.
5.@protocol和category中如何使用@property
在 protocol 中使用 property 只會生成 setter 和 getter 方法聲明,我們使用屬性的目的,是希望遵守我協(xié)議的對象能實(shí)現(xiàn)該屬性
category 使用 @property 也是只會生成 setter 和 getter 方法的聲明,如果我們真的需要給 category 增加屬性的實(shí)現(xiàn),需要借助于運(yùn)行時的兩個函數(shù):objc_setAssociatedObject和objc_getAssociatedObject
6.簡要說一下@autoreleasePool的數(shù)據(jù)結(jié)構(gòu)烛亦??
簡單說是雙向鏈表懂拾,每張鏈表頭尾相接煤禽,有 parent、child指針
每創(chuàng)建一個池子岖赋,會在首部創(chuàng)建一個 哨兵 對象,作為標(biāo)記
最外層池子的頂端會有一個next指針檬果。當(dāng)鏈表容量滿了,就會在鏈表的頂端唐断,并指向下一張表选脊。
7.BAD_ACCESS在什么情況下出現(xiàn)?
訪問了懸垂指針脸甘,比如對一個已經(jīng)釋放的對象執(zhí)行了release恳啥、訪問已經(jīng)釋放對象的成員變量或者發(fā)消息。 死循環(huán)
8.使用CADisplayLink丹诀、NSTimer有什么注意點(diǎn)钝的?
CADisplayLink、NSTimer會造成循環(huán)引用铆遭,可以使用YYWeakProxy或者為CADisplayLink硝桩、NSTimer添加block方法解決循環(huán)引用
9.iOS內(nèi)存分區(qū)情況
-
棧區(qū)(Stack)
由編譯器自動分配釋放,存放函數(shù)的參數(shù)枚荣,局部變量的值等
棧是向低地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu)碗脊,是一塊連續(xù)的內(nèi)存區(qū)域
-
堆區(qū)(Heap)
由程序員分配釋放
是向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域
-
全局區(qū)
全局變量和靜態(tài)變量的存儲是放在一塊的棍弄,初始化的全局變量和靜態(tài)變量在一塊區(qū)域望薄,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域
程序結(jié)束后由系統(tǒng)釋放
-
常量區(qū)
常量字符串就是放在這里的
程序結(jié)束后由系統(tǒng)釋放
-
代碼區(qū)
存放函數(shù)體的二進(jìn)制代碼
-
注:
在 iOS 中,堆區(qū)的內(nèi)存是應(yīng)用程序共享的呼畸,堆中的內(nèi)存分配是系統(tǒng)負(fù)責(zé)的
系統(tǒng)使用一個鏈表來維護(hù)所有已經(jīng)分配的內(nèi)存空間(系統(tǒng)僅僅記錄痕支,并不管理具體的內(nèi)容)
變量使用結(jié)束后,需要釋放內(nèi)存蛮原,OC 中是判斷引用計數(shù)是否為 0卧须,如果是就說明沒有任何變量使用該空間,那么系統(tǒng)將其回收
當(dāng)一個 app 啟動后,代碼區(qū)花嘶、常量區(qū)笋籽、全局區(qū)大小就已經(jīng)固定,因此指向這些區(qū)的指針不會產(chǎn)生崩潰性的錯誤椭员。而堆區(qū)和棧區(qū)是時時刻刻變化的(堆的創(chuàng)建銷毀车海,棧的彈入彈出),所以當(dāng)使用一個指針指向這個區(qū)里面的內(nèi)存時隘击,一定要注意內(nèi)存是否已經(jīng)被釋放侍芝,否則會產(chǎn)生程序崩潰(也即是野指針報錯)
10.iOS內(nèi)存管理方式
-
Tagged Pointer(小對象)
Tagged Pointer 專門用來存儲小的對象,例如 NSNumber 和 NSDate
Tagged Pointer 指針的值不再是地址了埋同,而是真正的值州叠。所以,實(shí)際上它不再是一個對象了凶赁,它只是一個披著對象皮的普通變量而已咧栗。所以,它的內(nèi)存并不存儲在堆中虱肄,也不需要 malloc 和 free
在內(nèi)存讀取上有著 3 倍的效率致板,創(chuàng)建時比以前快 106 倍
objc_msgSend 能識別 Tagged Pointer,比如 NSNumber 的 intValue 方法浩峡,直接從指針提取數(shù)據(jù)
使用 Tagged Pointer 后可岂,指針內(nèi)存儲的數(shù)據(jù)變成了 Tag + Data错敢,也就是將數(shù)據(jù)直接存儲在了指針中
-
NONPOINTER_ISA (指針中存放與該對象內(nèi)存相關(guān)的信息) 蘋果將 isa 設(shè)計成了聯(lián)合體翰灾,在 isa 中存儲了與該對象相關(guān)的一些內(nèi)存的信息,原因也如上面所說稚茅,并不需要 64 個二進(jìn)制位全部都用來存儲指針纸淮。
isa 的結(jié)構(gòu):
// x86_64 架構(gòu) struct { uintptr_t nonpointer : 1; // 0:普通指針,1:優(yōu)化過亚享,使用位域存儲更多信息 uintptr_t has_assoc : 1; // 對象是否含有或曾經(jīng)含有關(guān)聯(lián)引用 uintptr_t has_cxx_dtor : 1; // 表示是否有C++析構(gòu)函數(shù)或OC的dealloc uintptr_t shiftcls : 44; // 存放著 Class咽块、Meta-Class 對象的內(nèi)存地址信息 uintptr_t magic : 6; // 用于在調(diào)試時分辨對象是否未完成初始化 uintptr_t weakly_referenced : 1; // 是否被弱引用指向 uintptr_t deallocating : 1; // 對象是否正在釋放 uintptr_t has_sidetable_rc : 1; // 是否需要使用 sidetable 來存儲引用計數(shù) uintptr_t extra_rc : 8; // 引用計數(shù)能夠用 8 個二進(jìn)制位存儲時,直接存儲在這里 }; // arm64 架構(gòu) struct { uintptr_t nonpointer : 1; // 0:普通指針欺税,1:優(yōu)化過侈沪,使用位域存儲更多信息 uintptr_t has_assoc : 1; // 對象是否含有或曾經(jīng)含有關(guān)聯(lián)引用 uintptr_t has_cxx_dtor : 1; // 表示是否有C++析構(gòu)函數(shù)或OC的dealloc uintptr_t shiftcls : 33; // 存放著 Class、Meta-Class 對象的內(nèi)存地址信息 uintptr_t magic : 6; // 用于在調(diào)試時分辨對象是否未完成初始化 uintptr_t weakly_referenced : 1; // 是否被弱引用指向 uintptr_t deallocating : 1; // 對象是否正在釋放 uintptr_t has_sidetable_rc : 1; // 是否需要使用 sidetable 來存儲引用計數(shù) uintptr_t extra_rc : 19; // 引用計數(shù)能夠用 19 個二進(jìn)制位存儲時晚凿,直接存儲在這里 };
這里的 has_sidetable_rc 和 extra_rc亭罪,has_sidetable_rc 表明該指針是否引用了 sidetable 散列表,之所以有這個選項(xiàng)歼秽,是因?yàn)樯倭康囊糜嫈?shù)是不會直接存放在 SideTables 表中的应役,對象的引用計數(shù)會先存放在 extra_rc 中,當(dāng)其被存滿時,才會存入相應(yīng)的 SideTables 散列表中箩祥,SideTables 中有很多張 SideTable院崇,每個 SideTable 也都是一個散列表,而引用計數(shù)表就包含在 SideTable 之中袍祖。
-
散列表(引用計數(shù)表底瓣、弱引用表)
引用計數(shù)要么存放在 isa 的 extra_rc 中,要么存放在引用計數(shù)表中蕉陋,而引用計數(shù)表包含在一個叫 SideTable 的結(jié)構(gòu)中濒持,它是一個散列表,也就是哈希表寺滚。而 SideTable 又包含在一個全局的 StripeMap 的哈希映射表中柑营,這個表的名字叫 SideTables。
當(dāng)一個對象訪問 SideTables 時:
首先會取得對象的地址村视,將地址進(jìn)行哈希運(yùn)算官套,與 SideTables 中 SideTable 的個數(shù)取余,最后得到的結(jié)果就是該對象所要訪問的 SideTable
在取得的 SideTable 中的 RefcountMap 表中再進(jìn)行一次哈希查找蚁孔,找到該對象在引用計數(shù)表中對應(yīng)的位置
如果該位置存在對應(yīng)的引用計數(shù)奶赔,則對其進(jìn)行操作,如果沒有對應(yīng)的引用計數(shù)杠氢,則創(chuàng)建一個對應(yīng)的 size_t 對象站刑,其實(shí)就是一個 uint 類型的無符號整型
弱引用表也是一張哈希表的結(jié)構(gòu),其內(nèi)部包含了每個對象對應(yīng)的弱引用表 weak_entry_t鼻百,而 weak_entry_t 是一個結(jié)構(gòu)體數(shù)組绞旅,其中包含的則是每一個對象弱引用的對象所對應(yīng)的弱引用指針。
11.循環(huán)引用
循環(huán)引用的實(shí)質(zhì):多個對象相互之間有強(qiáng)引用温艇,不能釋放讓系統(tǒng)回收因悲。
如何解決循環(huán)引用?
1勺爱、避免產(chǎn)生循環(huán)引用晃琳,通常是將 strong 引用改為 weak 引用。 比如在修飾屬性時用weak 在block內(nèi)調(diào)用對象方法時琐鲁,使用其弱引用卫旱,這里可以使用兩個宏
#define WS(weakSelf) __weak __typeof(&*self)weakSelf = self; // 弱引用
#define ST(strongSelf) __strong __typeof(&*self)strongSelf = weakSelf; //使用這個要先聲明weakSelf 還可以使用__block來修飾變量 在MRC下,__block不會增加其引用計數(shù)围段,避免了循環(huán)引用 在ARC下顾翼,__block修飾對象會被強(qiáng)引用,無法避免循環(huán)引用蒜撮,需要手動解除暴构。
2跪呈、在合適時機(jī)去手動斷開循環(huán)引用。 通常我們使用第一種取逾。
-
代理(delegate)循環(huán)引用屬于相互循環(huán)引用
delegate 是iOS中開發(fā)中比較常遇到的循環(huán)引用耗绿,一般在聲明delegate的時候都要使用弱引用 weak,或者assign,當(dāng)然怎么選擇使用assign還是weak,MRC的話只能用assign砾隅,在ARC的情況下最好使用weak误阻,因?yàn)閣eak修飾的變量在釋放后自動指向nil,防止野指針存在
-
NSTimer循環(huán)引用屬于相互循環(huán)使用
在控制器內(nèi)晴埂,創(chuàng)建NSTimer作為其屬性究反,由于定時器創(chuàng)建后也會強(qiáng)引用該控制器對象,那么該對象和定時器就相互循環(huán)引用了儒洛。 如何解決呢精耐? 這里我們可以使用手動斷開循環(huán)引用: 如果是不重復(fù)定時器,在回調(diào)方法里將定時器invalidate并置為nil即可琅锻。 如果是重復(fù)定時器卦停,在合適的位置將其invalidate并置為nil即可
3、block循環(huán)引用
一個簡單的例子:
@property (copy, nonatomic) dispatch_block_t myBlock;
@property (copy, nonatomic) NSString *blockString;
- (void)testBlock {
self.myBlock = ^() {
NSLog(@"%@",self.blockString);
};
}
由于block會對block中的對象進(jìn)行持有操作,就相當(dāng)于持有了其中的對象恼蓬,而如果此時block中的對象又持有了該block惊完,則會造成循環(huán)引用。 解決方案就是使用__weak修飾self即可
__weak typeof(self) weakSelf = self;
self.myBlock = ^() {
NSLog(@"%@",weakSelf.blockString);
};
并不是所有block都會造成循環(huán)引用处硬。 只有被強(qiáng)引用了的block才會產(chǎn)生循環(huán)引用 而比如dispatch_async(dispatch_get_main_queue(), ^{}),[UIView animateWithDuration:1 animations:^{}]這些系統(tǒng)方法等 或者block并不是其屬性而是臨時變量,即棧block
[self testWithBlock:^{
NSLog(@"%@",self);
}];
- (void)testWithBlock:(dispatch_block_t)block {
block();
}
還有一種場景小槐,在block執(zhí)行開始時self對象還未被釋放,而執(zhí)行過程中荷辕,self被釋放了凿跳,由于是用weak修飾的,那么weakSelf也被釋放了桐腌,此時在block里訪問weakSelf時拄显,就可能會發(fā)生錯誤(向nil對象發(fā)消息并不會崩潰苟径,但也沒任何效果)案站。 對于這種場景,應(yīng)該在block中對 對象使用__strong修飾棘街,使得在block期間對 對象持有蟆盐,block執(zhí)行結(jié)束后,解除其持有遭殉。
__weak typeof(self) weakSelf = self;
self.myBlock = ^() {
__strong __typeof(self) strongSelf = weakSelf;
[strongSelf test];
};
<<<<<<< HEAD
>>>>>>> b3040bc46edebd638bd0855c05a48f9308af5781
消息傳遞的方式
1.KVC實(shí)現(xiàn)原理
KVC石挂,鍵-值編碼,使用字符串直接訪問對象的屬性险污。
-
底層實(shí)現(xiàn)痹愚,當(dāng)一個對象調(diào)用setValue方法時富岳,方法內(nèi)部會做以下操作:
1.檢查是否存在相應(yīng)key的set方法,如果存在拯腮,就調(diào)用set方法
2.如果set方法不存在窖式,就會查找與key相同名稱并且?guī)聞澗€的成員屬性,如果有动壤,則直接給成員屬性賦值
3.如果沒有找到_key萝喘,就會查找相同名稱的屬性key,如果有就直接賦值
4.如果還沒找到琼懊,則調(diào)用valueForUndefinedKey:和setValue:forUndefinedKey:方法
2.KVO的實(shí)現(xiàn)原理
[圖片上傳失敗...(image-d28773-1679319743059)]
KVO-鍵值觀察機(jī)制阁簸,原理如下:
1.當(dāng)給A類添加KVO的時候,runtime動態(tài)的生成了一個子類NSKVONotifying_A哼丈,讓A類的isa指針指向NSKVONotifying_A類启妹,重寫class方法,隱藏對象真實(shí)類信息
2.重寫監(jiān)聽屬性的setter方法醉旦,在setter方法內(nèi)部調(diào)用了Foundation 的 _NSSetObjectValueAndNotify 函數(shù)
-
3._NSSetObjectValueAndNotify函數(shù)內(nèi)部
a) 首先會調(diào)用 willChangeValueForKey
b) 然后給屬性賦值
c) 最后調(diào)用 didChangeValueForKey
d) 最后調(diào)用 observer 的 observeValueForKeyPath 去告訴監(jiān)聽器屬性值發(fā)生了改變 .
4.重寫了dealloc做一些 KVO 內(nèi)存釋放
3.如何手動觸發(fā)KVO方法
手動調(diào)用willChangeValueForKey和didChangeValueForKey方法
鍵值觀察通知依賴于 NSObject 的兩個方法: willChangeValueForKey: 和 didChangeValueForKey翅溺。在一個被觀察屬性發(fā)生改變之前互广, willChangeValueForKey: 一定會被調(diào)用喻频,這就 會記錄舊的值评疗。而當(dāng)改變發(fā)生后寻拂, didChangeValueForKey 會被調(diào)用征字,繼而 observeValueForKey:ofObject:change:context: 也會被調(diào)用防症。如果可以手動實(shí)現(xiàn)這些調(diào)用赌结,就可以實(shí)現(xiàn)“手動觸發(fā)”了 有人可能會問只調(diào)用didChangeValueForKey方法可以觸發(fā)KVO方法吃环,其實(shí)是不能的羹饰,因?yàn)閣illChangeValueForKey: 記錄舊的值伊滋,如果不記錄舊的值,那就沒有改變一說了
4.通知和代理有什么區(qū)別
通知是觀察者模式队秩,適合一對多的場景
代理模式適合一對一的反向傳值
通知耦合度低笑旺,代理的耦合度高
5.block和delegate的區(qū)別
-
delegate運(yùn)行成本低,block的運(yùn)行成本高
block出棧需要將使用的數(shù)據(jù)從棧內(nèi)存拷貝到堆內(nèi)存馍资,當(dāng)然對象的話就是加計數(shù)筒主,使用完或者block置nil后才消除。delegate只是保存了一個對象指針鸟蟹,直接回調(diào)乌妙,沒有額外消耗。就像C的函數(shù)指針建钥,只多做了一個查表動作藤韵。
delegate更適用于多個回調(diào)方法(3個以上),block則適用于1熊经,2個回調(diào)時泽艘。
6.為什么Block用copy關(guān)鍵字
Block在沒有使用外部變量時欲险,內(nèi)存存在全局區(qū),然而匹涮,當(dāng)Block在使用外部變量的時候盯荤,內(nèi)存是存在于棧區(qū),當(dāng)Block copy之后焕盟,是存在堆區(qū)的秋秤。存在于棧區(qū)的特點(diǎn)是對象隨時有可能被銷毀,一旦銷毀在調(diào)用的時候脚翘,就會造成系統(tǒng)的崩潰灼卢。所以Block要用copy關(guān)鍵字。
網(wǎng)絡(luò)
1.網(wǎng)絡(luò)七層協(xié)議
-
應(yīng)用層:
1.用戶接口来农、應(yīng)用程序鞋真;
2.Application典型設(shè)備:網(wǎng)關(guān);
3.典型協(xié)議沃于、標(biāo)準(zhǔn)和應(yīng)用:TELNET涩咖、FTP、HTTP
-
表示層:
1.數(shù)據(jù)表示繁莹、壓縮和加密presentation
2.典型設(shè)備:網(wǎng)關(guān)
3.典型協(xié)議檩互、標(biāo)準(zhǔn)和應(yīng)用:ASCLL、PICT咨演、TIFF闸昨、JPEG|MPEG
4.表示層相當(dāng)于一個東西的表示,表示的一些協(xié)議薄风,比如圖片饵较、聲音和視頻MPEG。
-
會話層:
1.會話的建立和結(jié)束遭赂;
2.典型設(shè)備:網(wǎng)關(guān)循诉;
3.典型協(xié)議、標(biāo)準(zhǔn)和應(yīng)用:RPC撇他、SQL茄猫、NFS、X WINDOWS逆粹、ASP
-
傳輸層:
1.主要功能:端到端控制Transport募疮;
2.典型設(shè)備:網(wǎng)關(guān);
3.典型協(xié)議僻弹、標(biāo)準(zhǔn)和應(yīng)用:TCP、UDP他嚷、SPX
-
網(wǎng)絡(luò)層:
1.主要功能:路由蹋绽、尋址Network芭毙;
2.典型設(shè)備:路由器;
3.典型協(xié)議卸耘、標(biāo)準(zhǔn)和應(yīng)用:IP退敦、IPX、APPLETALK蚣抗、ICMP侈百;
-
數(shù)據(jù)鏈路層:
1.主要功能:保證無差錯的疏忽鏈路的data link;
2.典型設(shè)備:交換機(jī)翰铡、網(wǎng)橋钝域、網(wǎng)卡;
3.典型協(xié)議锭魔、標(biāo)準(zhǔn)和應(yīng)用:802.2例证、802.3ATM、HDLC迷捧、FRAME RELAY织咧;
-
物理層:
1.主要功能:傳輸比特流Physical;
2.典型設(shè)備:集線器漠秋、中繼器
3.典型協(xié)議笙蒙、標(biāo)準(zhǔn)和應(yīng)用:V.35、EIA/TIA-232.
2.Http 和 Https 的區(qū)別庆锦?Https為什么更加安全手趣?
-
區(qū)別
1.HTTPS 需要向機(jī)構(gòu)申請 CA 證書,極少免費(fèi)肥荔。
2.HTTP 屬于明文傳輸绿渣,HTTPS基于 SSL 進(jìn)行加密傳輸。
3.HTTP 端口號為 80燕耿,HTTPS 端口號為 443 中符。
4.HTTPS 是加密傳輸,有身份驗(yàn)證的環(huán)節(jié)誉帅,更加安全淀散。
-
安全
SSL(安全套接層) TLS(傳輸層安全)
以上兩者在傳輸層之上,對網(wǎng)絡(luò)連接進(jìn)行加密處理蚜锨,保障數(shù)據(jù)的完整性档插,更加的安全。
3.HTTPS的連接建立流程
HTTPS為了兼顧安全與效率亚再,同時使用了對稱加密和非對稱加密郭膛。在傳輸?shù)倪^程中會涉及到三個密鑰:
服務(wù)器端的公鑰和私鑰,用來進(jìn)行非對稱加密
-
客戶端生成的隨機(jī)密鑰氛悬,用來進(jìn)行對稱加密
[圖片上傳失敗...(image-2381d6-1679319743055)]
如上圖则剃,HTTPS連接過程大致可分為八步:
-
1耘柱、客戶端訪問HTTPS連接。
客戶端會把安全協(xié)議版本號棍现、客戶端支持的加密算法列表调煎、隨機(jī)數(shù)C發(fā)給服務(wù)端。
-
2己肮、服務(wù)端發(fā)送證書給客戶端
服務(wù)端接收密鑰算法配件后士袄,會和自己支持的加密算法列表進(jìn)行比對,如果不符合谎僻,則斷開連接娄柳。否則,服務(wù)端會在該算法列表中戈稿,選擇一種對稱算法(如AES)西土、一種公鑰算法(如具有特定秘鑰長度的RSA)和一種MAC算法發(fā)給客戶端。
服務(wù)器端有一個密鑰對鞍盗,即公鑰和私鑰需了,是用來進(jìn)行非對稱加密使用的,服務(wù)器端保存著私鑰般甲,不能將其泄露肋乍,公鑰可以發(fā)送給任何人。
在發(fā)送加密算法的同時還會把數(shù)字證書和隨機(jī)數(shù)S發(fā)送給客戶端
-
3敷存、客戶端驗(yàn)證server證書
會對server公鑰進(jìn)行檢查墓造,驗(yàn)證其合法性,如果發(fā)現(xiàn)發(fā)現(xiàn)公鑰有問題锚烦,那么HTTPS傳輸就無法繼續(xù)觅闽。
-
4、客戶端組裝會話秘鑰
如果公鑰合格涮俄,那么客戶端會用服務(wù)器公鑰來生成一個前主秘鑰(Pre-Master Secret蛉拙,PMS),并通過該前主秘鑰和隨機(jī)數(shù)C彻亲、S來組裝成會話秘鑰
-
5孕锄、客戶端將前主秘鑰加密發(fā)送給服務(wù)端
是通過服務(wù)端的公鑰來對前主秘鑰進(jìn)行非對稱加密,發(fā)送給服務(wù)端
-
6苞尝、服務(wù)端通過私鑰解密得到前主秘鑰
服務(wù)端接收到加密信息后畸肆,用私鑰解密得到主秘鑰。
-
7宙址、服務(wù)端組裝會話秘鑰
服務(wù)端通過前主秘鑰和隨機(jī)數(shù)C轴脐、S來組裝會話秘鑰。
至此,服務(wù)端和客戶端都已經(jīng)知道了用于此次會話的主秘鑰豁辉。
-
8令野、數(shù)據(jù)傳輸
客戶端收到服務(wù)器發(fā)送來的密文舀患,用客戶端密鑰對其進(jìn)行對稱解密徽级,得到服務(wù)器發(fā)送的數(shù)據(jù)。
同理聊浅,服務(wù)端收到客戶端發(fā)送來的密文餐抢,用服務(wù)端密鑰對其進(jìn)行對稱解密,得到客戶端發(fā)送的數(shù)據(jù)低匙。
4.解釋一下 三次握手 和 四次揮手
-
三次握手
1.由客戶端向服務(wù)端發(fā)送 SYN 同步報文旷痕。
2.當(dāng)服務(wù)端收到 SYN 同步報文之后,會返回給客戶端 SYN 同步報文和 ACK 確認(rèn)報文顽冶。
3.客戶端會向服務(wù)端發(fā)送 ACK 確認(rèn)報文欺抗,此時客戶端和服務(wù)端的連接正式建立。
-
建立連接
1.這個時候客戶端就可以通過 Http 請求報文强重,向服務(wù)端發(fā)送請求
2.服務(wù)端接收到客戶端的請求之后绞呈,向客戶端回復(fù) Http 響應(yīng)報文。
-
四次揮手
當(dāng)客戶端和服務(wù)端的連接想要斷開的時候间景,要經(jīng)歷四次揮手的過程佃声,步驟如下:
1.先由客戶端向服務(wù)端發(fā)送 FIN 結(jié)束報文。
2.服務(wù)端會返回給客戶端 ACK 確認(rèn)報文 倘要。此時圾亏,由客戶端發(fā)起的斷開連接已經(jīng)完成。
3.服務(wù)端會發(fā)送給客戶端 FIN 結(jié)束報文 和 ACK 確認(rèn)報文封拧。
4.客戶端會返回 ACK 確認(rèn)報文到服務(wù)端志鹃,至此,由服務(wù)端方向的斷開連接已經(jīng)完成泽西。
5.TCP 和 UDP的區(qū)別
TCP:面向連接曹铃、傳輸可靠(保證數(shù)據(jù)正確性,保證數(shù)據(jù)順序)、用于傳輸大量數(shù)據(jù)(流模式)尝苇、速度慢铛只,建立連接需要開銷較多(時間,系統(tǒng)資源)糠溜。
UDP:面向非連接淳玩、傳輸不可靠、用于傳輸少量數(shù)據(jù)(數(shù)據(jù)包模式)非竿、速度快蜕着。
6.Cookie和Session
cookie
-
1.用戶與服務(wù)器的交互
cookie主要是用來記錄用戶狀態(tài),區(qū)分用戶,狀態(tài)保存在客戶端承匣。cookie功能需要瀏覽器的支持蓖乘。如果瀏覽器不支持cookie(如大部分手機(jī)中的瀏覽器)或者把cookie禁用了,cookie功能就會失效韧骗。
[圖片上傳失敗...(image-4ce4ea-1679319743055)]
a).首次訪問amazon時嘉抒,客戶端發(fā)送一個HTTP請求到服務(wù)器端 。服務(wù)器端發(fā)送一個HTTP響應(yīng)到客戶端袍暴,其中包含Set-Cookie頭部
b).客戶端發(fā)送一個HTTP請求到服務(wù)器端些侍,其中包含Cookie頭部。服務(wù)器端發(fā)送一個HTTP響應(yīng)到客戶端
c).隔段時間再去訪問時政模,客戶端會直接發(fā)包含Cookie頭部的HTTP請求岗宣。服務(wù)器端發(fā)送一個HTTP響應(yīng)到客戶端
-
2.cookie的修改和刪除
在修改cookie的時候,只需要新cookie覆蓋舊cookie即可淋样,在覆蓋的時候耗式,由于Cookie具有不可跨域名性,注意name趁猴、path刊咳、domain需與原cookie一致
刪除cookie也一樣,設(shè)置cookie的過期時間expires為過去的一個時間點(diǎn)躲叼,或者maxAge = 0(Cookie的有效期,單位為秒)即可
-
3芦缰、cookie的安全
事實(shí)上,cookie的使用存在爭議枫慷,因?yàn)樗徽J(rèn)為是對用戶隱私的一種侵害让蕾,而且cookie并不安全
HTTP協(xié)議不僅是無狀態(tài)的或听,而且是不安全的探孝。使用HTTP協(xié)議的數(shù)據(jù)不經(jīng)過任何加密就直接在網(wǎng)絡(luò)上傳播,有被截獲的可能誉裆。使用HTTP協(xié)議傳輸很機(jī)密的內(nèi)容是一種隱患顿颅。
a).如果不希望Cookie在HTTP等非安全協(xié)議中傳輸,可以設(shè)置Cookie的secure屬性為true足丢。瀏覽器只會在HTTPS和SSL等安全協(xié)議中傳輸此類Cookie粱腻。
b).此外,secure屬性并不能對Cookie內(nèi)容加密斩跌,因而不能保證絕對的安全性绍些。如果需要高安全性,需要在程序中對Cookie內(nèi)容加密耀鸦、解密柬批,以防泄密。
c).也可以設(shè)置cookie為HttpOnly,如果在cookie中設(shè)置了HttpOnly屬性氮帐,那么通過js腳本將無法讀取到cookie信息嗅虏,這樣能有效的防止XSS(跨站腳本攻擊)攻擊
Session
Session是服務(wù)器端使用的一種記錄客戶端狀態(tài)的機(jī)制,使用上比Cookie簡單一些上沐,相應(yīng)的也增加了服務(wù)器的存儲壓力皮服。
-
Session是另一種記錄客戶狀態(tài)的機(jī)制,不同的是Cookie保存在客戶端瀏覽器中奄容,而Session保存在服務(wù)器上冰更。 客戶端瀏覽器訪問服務(wù)器的時候产徊,服務(wù)器把客戶端信息以某種形式記錄在服務(wù)器上昂勒。這就是Session≈弁客戶端瀏覽器再次訪問時只需要從該Session中查找該客戶的狀態(tài)就可以了戈盈。
[圖片上傳失敗...(image-d5a83-1679319743055)]
當(dāng)程序需要為某個客戶端的請求創(chuàng)建一個session時,服務(wù)器首先檢查這個客戶端的請求里是否已包含了一個session標(biāo)識(稱為SessionId)
如果已包含則說明以前已經(jīng)為此客戶端創(chuàng)建過session谆刨,服務(wù)器就按照SessionId把這個session檢索出來塘娶,使用(檢索不到,會新建一個)
如果客戶端請求不包含SessionId痊夭,則為此客戶端創(chuàng)建一個session并且生成一個與此session相關(guān)聯(lián)的SessionId刁岸,SessionId的值應(yīng)該是一個既不會重復(fù),又不容易被找到規(guī)律以仿造的字符串她我,這個SessionId將被在本次響應(yīng)中返回給客戶端保存虹曙。
保存這個SessionId的方式可以采用cookie,這樣在交互過程中瀏覽器可以自動的按照規(guī)則把這個標(biāo)識發(fā)送給服務(wù)器番舆。但cookie可以被人為的禁止酝碳,則必須有其他機(jī)制以便在cookie被禁止時仍然能夠把SessionId傳遞回服務(wù)器。
Cookie 和Session 的區(qū)別:
1恨狈、cookie數(shù)據(jù)存放在客戶的瀏覽器上疏哗,session數(shù)據(jù)放在服務(wù)器上。
2禾怠、cookie相比session不是很安全返奉,別人可以分析存放在本地的cookie并進(jìn)行cookie欺騙,考慮到安全應(yīng)當(dāng)使用session。
3吗氏、session會在一定時間內(nèi)保存在服務(wù)器上芽偏。當(dāng)訪問增多,會比較占用你服務(wù)器的性能,考慮到減輕服務(wù)器性能方面牲证,應(yīng)當(dāng)使用cookie哮针。
4、單個cookie保存的數(shù)據(jù)不能超過4K,很多瀏覽器都限制一個站點(diǎn)最多保存20個cookie十厢。而session存儲在服務(wù)端等太,可以無限量存儲
5、所以:將登錄信息等重要信息存放為session;其他信息如果需要保留蛮放,可以放在cookie中
7.DNS是什么
因特網(wǎng)上的主機(jī)缩抡,可以使用多種方式標(biāo)識蜘腌,比如主機(jī)名或IP地址愚战。一種標(biāo)識方法就是用它的主機(jī)名(hostname),比如·www.baidu.com杆煞、www.google.com娩嚼、gaia.cs.umass.edu等蘑险。這方式方便人們記憶和接受,但是這種長度不一岳悟、沒有規(guī)律的字符串路由器并不方便處理佃迄。還有一種方式,就是直接使用定長的贵少、有著清晰層次結(jié)構(gòu)的IP地址呵俏,路由器比較熱衷于這種方式。為了折衷這兩種方式滔灶,我們需要一種能進(jìn)行主機(jī)名到IP地址轉(zhuǎn)換的目錄服務(wù)普碎。這就是域名系統(tǒng)(Domain Name System,DNS)的主要任務(wù)录平。
-
DNS是:
1麻车、一個由分層的DNS服務(wù)器實(shí)現(xiàn)的分布式數(shù)據(jù)庫
2、一個使得主機(jī)能夠查詢分布式數(shù)據(jù)庫的應(yīng)用層協(xié)議
DNS服務(wù)器通常是運(yùn)行BIND軟件的UNIX機(jī)器萄涯,DNS協(xié)議運(yùn)行在UDP上绪氛,使用53號端口
DNS通常是由其他應(yīng)用層協(xié)議所使用的,包括HTTP涝影、SMTP等枣察。其作用則是:將用戶提供的主機(jī)名解析為IP地址
-
DNS的一種簡單設(shè)計就是在因特網(wǎng)上只使用一個DNS服務(wù)器,該服務(wù)器包含所有的映射燃逻。很明顯這種設(shè)計是有很大的問題的:
單點(diǎn)故障:如果該DNS服務(wù)器崩潰序目,全世界的網(wǎng)絡(luò)隨之癱瘓
通信容量:單個DNS服務(wù)器必須處理所有DNS查詢
遠(yuǎn)距離的集中式數(shù)據(jù)庫:單個DNS服務(wù)器必須面對所有用戶,距離過遠(yuǎn)會有嚴(yán)重的時延伯襟。
維護(hù):該數(shù)據(jù)庫過于龐大猿涨,還需要對新添加的主機(jī)頻繁更新。
所以叛赚,DNS被設(shè)計成了一個分布式俺附、層次數(shù)據(jù)庫
8.DNS解析過程
以www.163.com為例:
客戶端打開瀏覽器,輸入一個域名步鉴。比如輸入www.163.com,這時氛琢,客戶端會發(fā)出一個DNS請求到本地DNS服務(wù)器。本地DNS服務(wù)器一般都是你的網(wǎng)絡(luò)接入服務(wù)器商提供障般,比如中國電信,中國移動即供。
查詢www.163.com的DNS請求到達(dá)本地DNS服務(wù)器之后,本地DNS服務(wù)器會首先查詢它的緩存記錄驱证,如果緩存中有此條記錄,就可以直接返回結(jié)果伙单。如果沒有,本地DNS服務(wù)器還要向DNS根服務(wù)器進(jìn)行查詢布疼。
根DNS服務(wù)器沒有記錄具體的域名和IP地址的對應(yīng)關(guān)系严就,而是告訴本地DNS服務(wù)器梢为,你可以到域服務(wù)器上去繼續(xù)查詢,并給出域服務(wù)器的地址粟害。
本地DNS服務(wù)器繼續(xù)向域服務(wù)器發(fā)出請求悲幅,在這個例子中,請求的對象是.com域服務(wù)器留荔。.com域服務(wù)器收到請求之后聚蝶,也不會直接返回域名和IP地址的對應(yīng)關(guān)系碘勉,而是告訴本地DNS服務(wù)器,你的域名的解析服務(wù)器的地址。
最后兼蕊,本地DNS服務(wù)器向域名的解析服務(wù)器發(fā)出請求产禾,這時就能收到一個域名和IP地址對應(yīng)關(guān)系亚情,本地DNS服務(wù)器不僅要把IP地址返回給用戶電腦楞件,還要把這個對應(yīng)關(guān)系保存在緩存中,以備下次別的用戶查詢時黄伊,可以直接返回結(jié)果,加快網(wǎng)絡(luò)訪問拓轻。
數(shù)據(jù)存儲
1.iOS 開發(fā)中數(shù)據(jù)持久性有哪幾種?
iOS本地數(shù)據(jù)保存有多種方式,比如NSUserDefaults、歸檔粱甫、文件保存危纫、數(shù)據(jù)庫、CoreData螃征、KeyChain(鑰匙串)等多種方式踢械。其中KeyChain(鑰匙串)是保存到沙盒范圍以外的地方,也就是與沙盒無關(guān)。
2.FMDB數(shù)據(jù)結(jié)構(gòu)變化升級
1.使用columnExists:inTableWithName方法判斷數(shù)據(jù)表中是否存在字段
2.如果不存在交排,則添加, 如:向bbb表中添加aaa字段 -> ALTER TABLE bbb ADD 'aaa' TEXT
多線程
1.進(jìn)程與線程
-
進(jìn)程:
1.進(jìn)程是一個具有一定獨(dú)立功能的程序關(guān)于某次數(shù)據(jù)集合的一次運(yùn)行活動翻翩,它是操作系統(tǒng)分配資源的基本單元.
2.進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個應(yīng)用程序胶征,就是一段程序的執(zhí)行過程,我們可以理解為手機(jī)上的一個app.
3.每個進(jìn)程之間是獨(dú)立的,每個進(jìn)程均運(yùn)行在其專用且受保護(hù)的內(nèi)存空間內(nèi)服傍,擁有獨(dú)立運(yùn)行所需的全部資源
-
線程
1.程序執(zhí)行流的最小單元,線程是進(jìn)程中的一個實(shí)體.
2.一個進(jìn)程要想執(zhí)行任務(wù),必須至少有一條線程.應(yīng)用程序啟動的時候套蒂,系統(tǒng)會默認(rèn)開啟一條線程,也就是主線程
-
進(jìn)程和線程的關(guān)系
1.線程是進(jìn)程的執(zhí)行單元,進(jìn)程的所有任務(wù)都在線程中執(zhí)行
2.線程是 CPU 分配資源和調(diào)度的最小單位
3.一個程序可以對應(yīng)多個進(jìn)程(多進(jìn)程),一個進(jìn)程中可有多個線程,但至少要有一條線程
4.同一個進(jìn)程內(nèi)的線程共享進(jìn)程資源
2.什么是多線程?
多線程的實(shí)現(xiàn)原理:事實(shí)上静稻,同一時間內(nèi)單核的CPU只能執(zhí)行一個線程,多線程是CPU快速的在多個線程之間進(jìn)行切換(調(diào)度)押搪,造成了多個線程同時執(zhí)行的假象垂谢。
如果是多核CPU就真的可以同時處理多個線程了根暑。
多線程的目的是為了同步完成多項(xiàng)任務(wù),通過提高系統(tǒng)的資源利用率來提高系統(tǒng)的效率淳地。
3.多線程的優(yōu)點(diǎn)和缺點(diǎn)
-
優(yōu)點(diǎn):
能適當(dāng)提高程序的執(zhí)行效率
能適當(dāng)提高資源利用率(CPU、內(nèi)存利用率)
-
缺點(diǎn):
開啟線程需要占用一定的內(nèi)存空間(默認(rèn)情況下,主線程占用1M阅爽,子線程占用512KB)简肴,如果開啟大量的線程能扒,會占用大量的內(nèi)存空間,降低程序的性能
線程越多,CPU在調(diào)度線程上的開銷就越大
程序設(shè)計更加復(fù)雜:比如線程之間的通信、多線程的數(shù)據(jù)共享
4.多線程的 并行 和 并發(fā) 有什么區(qū)別测摔?
并行:充分利用計算機(jī)的多核修噪,在多個線程上同步進(jìn)行
并發(fā):在一條線程上通過快速切換樊销,讓人感覺在同步進(jìn)行
5.iOS中實(shí)現(xiàn)多線程的幾種方案,各自有什么特點(diǎn)?
NSThread 面向?qū)ο蟮南僬迹枰绦騿T手動創(chuàng)建線程积蔚,但不需要手動銷毀怎顾。子線程間通信很難夭委。
GCD c語言,充分利用了設(shè)備的多核,自動管理線程生命周期。比NSOperation效率更高淑翼。
NSOperation 基于gcd封裝肉瓦,更加面向?qū)ο竽牡瘢萭cd多了一些功能。
6.多個網(wǎng)絡(luò)請求完成后執(zhí)行下一步
-
使用GCD的dispatch_group_t
創(chuàng)建一個dispatch_group_t
每次網(wǎng)絡(luò)請求前先dispatch_group_enter,請求回調(diào)后再dispatch_group_leave,enter和leave必須配合使用,有幾次enter就要有幾次leave,否則group會一直存在咨油。
當(dāng)所有enter的block都leave后棉胀,會執(zhí)行dispatch_group_notify的block霎挟。
NSString *str = @"http://xxxx.com/"; NSURL *url = [NSURL URLWithString:str]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; NSURLSession *session = [NSURLSession sharedSession]; dispatch_group_t downloadGroup = dispatch_group_create(); for (int i=0; i<10; i++) { dispatch_group_enter(downloadGroup); NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { NSLog(@"%d---%d",i,i); dispatch_group_leave(downloadGroup); }]; [task resume]; } dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{ NSLog(@"end"); });
-
使用GCD的信號量dispatch_semaphore_t
dispatch_semaphore信號量為基于計數(shù)器的一種多線程同步機(jī)制脊奋。如果semaphore計數(shù)大于等于1讶隐,計數(shù)-1,返回,程序繼續(xù)運(yùn)行。如果計數(shù)為0,則等待。dispatch_semaphore_signal(semaphore)為計數(shù)+1操作,dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER)為設(shè)置等待時間敬尺,這里設(shè)置的等待時間是一直等待。
創(chuàng)建semaphore為0,等待,等10個網(wǎng)絡(luò)請求都完成了赎瑰,dispatch_semaphore_signal(semaphore)為計數(shù)+1啤咽,然后計數(shù)-1返回
NSString *str = @"http://xxxx.com/"; NSURL *url = [NSURL URLWithString:str]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; NSURLSession *session = [NSURLSession sharedSession]; dispatch_semaphore_t sem = dispatch_semaphore_create(0); for (int i=0; i<10; i++) { NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { NSLog(@"%d---%d",i,i); count++; if (count==10) { dispatch_semaphore_signal(sem); count = 0; } }]; [task resume]; } dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"end"); });
7.多個網(wǎng)絡(luò)請求順序執(zhí)行后執(zhí)行下一步
-
使用信號量semaphore
每一次遍歷,都讓其dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER),這個時候線程會等待,阻塞當(dāng)前線程,直到dispatch_semaphore_signal(sem)調(diào)用之后
NSString *str = @"http://www.reibang.com/p/6930f335adba"; NSURL *url = [NSURL URLWithString:str]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; NSURLSession *session = [NSURLSession sharedSession]; dispatch_semaphore_t sem = dispatch_semaphore_create(0); for (int i=0; i<10; i++) { NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { NSLog(@"%d---%d",i,i); dispatch_semaphore_signal(sem); }]; [task resume]; dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); } dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"end"); });
8.異步操作兩組數(shù)據(jù)時, 執(zhí)行完第一組之后, 才能執(zhí)行第二組
-
這里使用dispatch_barrier_async柵欄方法即可實(shí)現(xiàn)
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"第一次任務(wù)的主線程為: %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"第二次任務(wù)的主線程為: %@", [NSThread currentThread]); }); dispatch_barrier_async(queue, ^{ NSLog(@"第一次任務(wù), 第二次任務(wù)執(zhí)行完畢, 繼續(xù)執(zhí)行"); }); dispatch_async(queue, ^{ NSLog(@"第三次任務(wù)的主線程為: %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"第四次任務(wù)的主線程為: %@", [NSThread currentThread]); });
9.多線程中的死鎖坊饶?
死鎖是由于多個線程(進(jìn)程)在執(zhí)行過程中蟋滴,因?yàn)闋帄Z資源而造成的互相等待現(xiàn)象,你可以理解為卡主了散庶。產(chǎn)生死鎖的必要條件有四個:
互斥條件 : 指進(jìn)程對所分配到的資源進(jìn)行排它性使用屋讶,即在一段時間內(nèi)某資源只由一個進(jìn)程占用轻腺。如果此時還有其它進(jìn)程請求資源挤土,則請求者只能等待儿礼,直至占有資源的進(jìn)程用畢釋放。
請求和保持條件 : 指進(jìn)程已經(jīng)保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進(jìn)程占有墨叛,此時請求進(jìn)程阻塞,但又對自己已獲得的其它資源保持不放。
不可剝奪條件 : 指進(jìn)程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放菠镇。
-
環(huán)路等待條件 : 指在發(fā)生死鎖時,必然存在一個進(jìn)程——資源的環(huán)形鏈嫉沽,即進(jìn)程集合{P0辟犀,P1绸硕,P2堂竟,···,Pn}中的P0正在等待一個P1占用的資源玻佩;P1正在等待P2占用的資源出嘹,……,Pn正在等待已被P0占用的資源咬崔。
最常見的就是 同步函數(shù) + 主隊列 的組合税稼,本質(zhì)是隊列阻塞。
dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"2"); }); NSLog(@"1"); // 什么也不會打印垮斯,直接報錯
10.GCD執(zhí)行原理郎仆?
GCD有一個底層線程池,這個池中存放的是一個個的線程兜蠕。之所以稱為“池”扰肌,很容易理解出這個“池”中的線程是可以重用的,當(dāng)一段時間后這個線程沒有被調(diào)用胡話熊杨,這個線程就會被銷毀曙旭。注意:開多少條線程是由底層線程池決定的(線程建議控制再3~5條)盗舰,池是系統(tǒng)自動來維護(hù),不需要我們程序員來維護(hù)(看到這句話是不是很開心桂躏?) 而我們程序員需要關(guān)心的是什么呢钻趋?我們只關(guān)心的是向隊列中添加任務(wù),隊列調(diào)度即可剂习。
如果隊列中存放的是同步任務(wù)蛮位,則任務(wù)出隊后,底層線程池中會提供一條線程供這個任務(wù)執(zhí)行鳞绕,任務(wù)執(zhí)行完畢后這條線程再回到線程池土至。這樣隊列中的任務(wù)反復(fù)調(diào)度,因?yàn)槭峭降幕ィ援?dāng)我們用currentThread打印的時候,就是同一條線程骡苞。
如果隊列中存放的是異步的任務(wù)垂蜗,(注意異步可以開線程),當(dāng)任務(wù)出隊后解幽,底層線程池會提供一個線程供任務(wù)執(zhí)行贴见,因?yàn)槭钱惒綀?zhí)行,隊列中的任務(wù)不需等待當(dāng)前任務(wù)執(zhí)行完畢就可以調(diào)度下一個任務(wù)躲株,這時底層線程池中會再次提供一個線程供第二個任務(wù)執(zhí)行片部,執(zhí)行完畢后再回到底層線程池中。
這樣就對線程完成一個復(fù)用霜定,而不需要每一個任務(wù)執(zhí)行都開啟新的線程档悠,也就從而節(jié)約的系統(tǒng)的開銷,提高了效率望浩。在iOS7.0的時候辖所,使用GCD系統(tǒng)通常只能開58條線程,iOS8.0以后磨德,系統(tǒng)可以開啟很多條線程缘回,但是實(shí)在開發(fā)應(yīng)用中,建議開啟線程條數(shù):35條最為合理典挑。
動畫
1.UIView動畫與核心動畫的區(qū)別?
- 核心動畫只作用在layer.
- 核心動畫修改的值都是假像.它的真實(shí)位置沒有發(fā)生變化.
- 當(dāng)需要與用戶進(jìn)行交互時用UIView動畫,不需要與用戶進(jìn)行交互時兩個都可以.
2.當(dāng)我們要做一些基于 CALayer 的動畫時酥宴,有時需要設(shè)置 layer 的錨點(diǎn)來配合動畫,這時候我們需要注意什么您觉?
-
需要注意的是設(shè)置錨點(diǎn)會引起原來 position 的變化拙寡,可能會發(fā)生不符合預(yù)期的行為,所以要做一下轉(zhuǎn)化顾犹,示例代碼如下:
// 為 layer 的動畫設(shè)置不同的 anchor point倒庵,但是又不想改變 view 原來的 position褒墨,則需要做一些轉(zhuǎn)換。 - (void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view { // 分別計算原來錨點(diǎn)和將更新的錨點(diǎn)對應(yīng)的坐標(biāo)點(diǎn)擎宝,這些坐標(biāo)點(diǎn)是相對該 view 內(nèi)部坐標(biāo)系的郁妈。 CGPoint oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x, view.bounds.size.height * view.layer.anchorPoint.y); CGPoint newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x, view.bounds.size.height * anchorPoint.y); // 如果當(dāng)前 view 有做過 transform,這里要同步計算绍申。 oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform); newPoint = CGPointApplyAffineTransform(newPoint, view.transform); // position 是當(dāng)前 view 的 anchor point 在其父 view 的位置噩咪。 CGPoint position = view.layer.position; // anchor point 的改變會造成 position 的改變,從而影響 view 在其父 view 的位置极阅,這里把這個位移給計算回來胃碾。 position.x = position.x + newPoint.x - oldPoint.x; position.y = position.y + newPoint.y - oldPoint.y; view.translatesAutoresizingMaskIntoConstraints = YES; view.layer.anchorPoint = anchorPoint; // 設(shè)置了新的 anchor point 會改變位置。 view.layer.position = position; // 通過在 position 上做逆向偏移筋搏,把位置給移回來仆百。 }
圖像處理
1.圖像的壓縮方式
-
壓縮圖片質(zhì)量
一般情況下使用UIImageJPEGRepresentation或UIImagePNGRepresentation方法實(shí)現(xiàn)。
-
壓縮圖片尺寸
一般通過指定壓縮的大小對圖像進(jìn)行重繪
2.如何計算圖片加載內(nèi)存中所占的大小
圖片內(nèi)存大小的計算公式 寬度 * 高度 * bytesPerPixel/8奔脐。
bytesPerPixel : 每個像素所占的字節(jié)數(shù)俄周。
RGBA顏色空間下 每個顏色分量由32位組成
所以一般圖片的計算公式是 wxhx4
數(shù)據(jù)安全及加密
1.對稱加密和非對稱加密的區(qū)別?
1髓迎、對稱加密又稱公開密鑰加密峦朗,加密和解密都會用到同一個密鑰,如果密鑰被攻擊者獲得排龄,此時加密就失去了意義波势。常見的對稱加密算法有DES、3DES橄维、AES尺铣、Blowfish、IDEA挣郭、RC5迄埃、RC6。
2兑障、非對稱加密又稱共享密鑰加密侄非,使用一對非對稱的密鑰,一把叫做私有密鑰流译,另一把叫做公有密鑰逞怨;公鑰加密只能用私鑰來解密,私鑰加密只能用公鑰來解密福澡。常見的公鑰加密算法有:RSA叠赦、ElGamal、背包算法、Rabin(RSA的特例)除秀、迪菲-赫爾曼密鑰交換協(xié)議中的公鑰加密算法糯累、橢圓曲線加密算法)。
2.簡述 SSL 加密的過程用了哪些加密方法册踩,為何這么作泳姐?
SSL 加密,在過程中實(shí)際使用了 對稱加密 和 非對稱加密 的結(jié)合暂吉。主要的考慮是先使用 非對稱加密 進(jìn)行連接胖秒,這樣做是為了避免中間人攻擊秘鑰被劫持,但是 非對稱加密 的效率比較低慕的。所以一旦建立了安全的連接之后阎肝,就可以使用輕量的 對稱加密。
3.iOS的簽名機(jī)制是怎么樣的
-
簽名機(jī)制:
先將應(yīng)用內(nèi)容通過摘要算法肮街,得到摘要
再用私鑰對摘要進(jìn)行加密得到密文
將源文本风题、密文、和私鑰對應(yīng)的公鑰一并發(fā)布
-
驗(yàn)證流程:
查看公鑰是否是私鑰方的
然后用公鑰對密文進(jìn)行解密得到摘要
將APP用同樣的摘要算法得到摘要嫉父,兩個摘要進(jìn)行比對俯邓,如果相等那么一切正常
Runtime
1.Category 的實(shí)現(xiàn)原理?
Category 實(shí)際上是 Category_t的結(jié)構(gòu)體熔号,在運(yùn)行時,新添加的方法鸟整,都被以倒序插入到原有方法列表的最前面引镊,所以不同的Category,添加了同一個方法篮条,執(zhí)行的實(shí)際上是最后一個弟头。
Category 在剛剛編譯完的時候,和原來的類是分開的涉茧,只有在程序運(yùn)行起來后赴恨,通過 Runtime ,Category 和原來的類才會合并到一起伴栓。
2.isa指針的理解伦连,對象的isa指針指向哪里?isa指針有哪兩種類型钳垮?
-
isa 等價于 is kind of
實(shí)例對象的 isa 指向類對象
類對象的 isa 指向元類對象
元類對象的 isa 指向元類的基類
-
isa 有兩種類型
純指針惑淳,指向內(nèi)存地址
NON_POINTER_ISA,除了內(nèi)存地址饺窿,還存有一些其他信息
3.Objective-C 如何實(shí)現(xiàn)多重繼承歧焦?
Object-c的類沒有多繼承,只支持單繼承,如果要實(shí)現(xiàn)多繼承的話,可使用如下幾種方式間接實(shí)現(xiàn)
-
通過組合實(shí)現(xiàn)
A和B組合肚医,作為C類的組件
-
通過協(xié)議實(shí)現(xiàn)
C類實(shí)現(xiàn)A和B類的協(xié)議方法
-
消息轉(zhuǎn)發(fā)實(shí)現(xiàn)
forwardInvocation:方法
4.runtime 如何實(shí)現(xiàn) weak 屬性绢馍?
weak 此特質(zhì)表明該屬性定義了一種「非擁有關(guān)系」(nonowning relationship)枫耳。為這種屬性設(shè)置新值時,設(shè)置方法既不持有新值(新指向的對象)嗽冒,也不釋放舊值(原來指向的對象)就珠。
runtime 對注冊的類,會進(jìn)行內(nèi)存布局舵稠,從一個粗粒度的概念上來講超升,這時候會有一個 hash 表,這是一個全局表哺徊,表中是用 weak 指向的對象內(nèi)存地址作為 key室琢,用所有指向該對象的 weak 指針表作為 value。當(dāng)此對象的引用計數(shù)為 0 的時候會 dealloc落追,假如該對象內(nèi)存地址是 a盈滴,那么就會以 a 為 key,在這個 weak 表中搜索轿钠,找到所有以 a 為鍵的 weak 對象巢钓,從而設(shè)置為 nil。
runtime 如何實(shí)現(xiàn) weak 屬性具體流程大致分為 3 步:
1疗垛、初始化時:runtime 會調(diào)用 objc_initWeak 函數(shù)症汹,初始化一個新的 weak 指針指向?qū)ο蟮牡刂贰?/p>
2、添加引用時:objc_initWeak 函數(shù)會調(diào)用 objc_storeWeak() 函數(shù)贷腕,objc_storeWeak() 的作用是更新指針指向(指針可能原來指向著其他對象背镇,這時候需要將該 weak 指針與舊對象解除綁定,會調(diào)用到 weak_unregister_no_lock)泽裳,如果指針指向的新對象非空瞒斩,則創(chuàng)建對應(yīng)的弱引用表,將 weak 指針與新對象進(jìn)行綁定涮总,會調(diào)用到 weak_register_no_lock胸囱。在這個過程中,為了防止多線程中競爭沖突瀑梗,會有一些鎖的操作烹笔。
3、釋放時:調(diào)用 clearDeallocating 函數(shù)抛丽,clearDeallocating 函數(shù)首先根據(jù)對象地址獲取所有 weak 指針地址的數(shù)組箕宙,然后遍歷這個數(shù)組把其中的數(shù)據(jù)設(shè)為 nil,最后把這個 entry 從 weak 表中刪除铺纽,最后清理對象的記錄柬帕。
5.講一下 OC 的消息機(jī)制
OC中的方法調(diào)用其實(shí)都是轉(zhuǎn)成了objc_msgSend函數(shù)的調(diào)用,給receiver(方法調(diào)用者)發(fā)送了一條消息(selector方法名)
objc_msgSend底層有3大階段,消息發(fā)送(當(dāng)前類陷寝、父類中查找)锅很、動態(tài)方法解析、消息轉(zhuǎn)發(fā)
6.runtime具體應(yīng)用
利用關(guān)聯(lián)對象(AssociatedObject)給分類添加屬性
遍歷類的所有成員變量(修改textfield的占位文字顏色凤跑、字典轉(zhuǎn)模型爆安、自動歸檔解檔)
交換方法實(shí)現(xiàn)(交換系統(tǒng)的方法)
利用消息轉(zhuǎn)發(fā)機(jī)制解決方法找不到的異常問題
KVC 字典轉(zhuǎn)模型
7.runtime如何通過selector找到對應(yīng)的IMP地址?
每一個類對象中都一個對象方法列表(對象方法緩存)
類方法列表是存放在類對象中isa指針指向的元類對象中(類方法緩存)仔引。
方法列表中每個方法結(jié)構(gòu)體中記錄著方法的名稱,方法實(shí)現(xiàn),以及參數(shù)類型扔仓,其實(shí)selector本質(zhì)就是方法名稱,通過這個方法名稱就可以在方法列表中找到對應(yīng)的方法實(shí)現(xiàn)。
當(dāng)我們發(fā)送一個消息給一個NSObject對象時咖耘,這條消息會在對象的類對象方法列表里查找翘簇。
當(dāng)我們發(fā)送一個消息給一個類時,這條消息會在類的Meta Class對象的方法列表里查找儿倒。
8.簡述下Objective-C中調(diào)用方法的過程
Objective-C是動態(tài)語言版保,每個方法在運(yùn)行時會被動態(tài)轉(zhuǎn)為消息發(fā)送,即:objc_msgSend(receiver, selector)夫否,整個過程介紹如下:
objc在向一個對象發(fā)送消息時彻犁,runtime庫會根據(jù)對象的isa指針找到該對象實(shí)際所屬的類
然后在該類中的方法列表以及其父類方法列表中尋找方法運(yùn)行
如果,在最頂層的父類(一般也就NSObject)中依然找不到相應(yīng)的方法時凰慈,程序在運(yùn)行時會掛掉并拋出異常unrecognized selector sent to XXX
但是在這之前汞幢,objc的運(yùn)行時會給出三次拯救程序崩潰的機(jī)會,這三次拯救程序奔潰的說明見問題《什么時候會報unrecognized selector的異澄⑽剑》中的說明急鳄。
9.load和initialize的區(qū)別
兩者都會自動調(diào)用父類的,不需要super操作堰酿,且僅會調(diào)用一次(不包括外部顯示調(diào)用).
load和initialize方法都會在實(shí)例化對象之前調(diào)用,以main函數(shù)為分水嶺张足,前者在main函數(shù)之前調(diào)用触创,后者在之后調(diào)用。這兩個方法會被自動調(diào)用为牍,不能手動調(diào)用它們哼绑。
load和initialize方法都不用顯示的調(diào)用父類的方法而是自動調(diào)用,即使子類沒有initialize方法也會調(diào)用父類的方法碉咆,而load方法則不會調(diào)用父類抖韩。
load方法通常用來進(jìn)行Method Swizzle,initialize方法一般用于初始化全局變量或靜態(tài)變量疫铜。
load和initialize方法內(nèi)部使用了鎖茂浮,因此它們是線程安全的。實(shí)現(xiàn)時要盡可能保持簡單,避免阻塞線程席揽,不要再使用鎖顽馋。
10.怎么理解Objective-C是動態(tài)運(yùn)行時語言。
主要是將數(shù)據(jù)類型的確定由編譯時,推遲到了運(yùn)行時幌羞。這個問題其實(shí)淺涉及到兩個概念,運(yùn)行時和多態(tài)寸谜。
簡單來說, 運(yùn)行時機(jī)制使我們直到運(yùn)行時才去決定一個對象的類別,以及調(diào)用該類別對象指定方法。
多態(tài):不同對象以自己的方式響應(yīng)相同的消息的能力叫做多態(tài)属桦。
意思就是假設(shè)生物類(life)都擁有一個相同的方法-eat;那人類屬于生物,豬也屬于生物,都繼承了life后,實(shí)現(xiàn)各自的eat,但是調(diào)用是我們只需調(diào)用各自的eat方法熊痴。也就是不同的對象以自己的方式響應(yīng)了相同的消 息(響應(yīng)了eat這個選擇器)。因此也可以說,運(yùn)行時機(jī)制是多態(tài)的基礎(chǔ).
Runloop
1.Runloop 和線程的關(guān)系聂宾?
一個線程對應(yīng)一個 Runloop果善。
主線程的默認(rèn)就有了 Runloop。
子線程的 Runloop 以懶加載的形式創(chuàng)建亏吝。
Runloop 存儲在一個全局的可變字典里岭埠,線程是 key ,Runloop 是 value蔚鸥。
2.RunLoop的運(yùn)行模式
-
RunLoop的運(yùn)行模式共有5種惜论,RunLoop只會運(yùn)行在一個模式下,要切換模式止喷,就要暫停當(dāng)前模式馆类,重寫啟動一個運(yùn)行模式
- kCFRunLoopDefaultMode, App的默認(rèn)運(yùn)行模式,通常主線程是在這個運(yùn)行模式下運(yùn)行 - UITrackingRunLoopMode, 跟蹤用戶交互事件(用于 ScrollView 追蹤觸摸滑動弹谁,保證界面滑動時不受其他Mode影響) - kCFRunLoopCommonModes, 偽模式乾巧,不是一種真正的運(yùn)行模式 - UIInitializationRunLoopMode:在剛啟動App時第進(jìn)入的第一個Mode,啟動完成后就不再使用 - GSEventReceiveRunLoopMode:接受系統(tǒng)內(nèi)部事件预愤,通常用不到
3.runloop內(nèi)部邏輯沟于?
-
實(shí)際上 RunLoop 就是這樣一個函數(shù),其內(nèi)部是一個 do-while 循環(huán)植康。當(dāng)你調(diào)用 CFRunLoopRun() 時旷太,線程就會一直停留在這個循環(huán)里;直到超時或被手動停止销睁,該函數(shù)才會返回供璧。
[圖片上傳失敗...(image-c62ef3-1679319743054)]
-
內(nèi)部邏輯:
通知 Observer 已經(jīng)進(jìn)入了 RunLoop
通知 Observer 即將處理 Timer
通知 Observer 即將處理非基于端口的輸入源(即將處理 Source0)
處理那些準(zhǔn)備好的非基于端口的輸入源(處理 Source0)
如果基于端口的輸入源準(zhǔn)備就緒并等待處理,請立刻處理該事件冻记。轉(zhuǎn)到第 9 步(處理 Source1)
通知 Observer 線程即將休眠
-
將線程置于休眠狀態(tài)睡毒,直到發(fā)生以下事件之一
事件到達(dá)基于端口的輸入源(port-based input sources)(也就是 Source0)
Timer 到時間執(zhí)行
外部手動喚醒
為 RunLoop 設(shè)定的時間超時
通知 Observer 線程剛被喚醒(還沒處理事件)
-
處理待處理事件
如果是 Timer 事件,處理 Timer 并重新啟動循環(huán)冗栗,跳到第 2 步
如果輸入源被觸發(fā)演顾,處理該事件(文檔上是 deliver the event)
如果 RunLoop 被手動喚醒但尚未超時供搀,重新啟動循環(huán),跳到第 2 步
4.autoreleasePool 在何時被釋放偶房?
App啟動后趁曼,蘋果在主線程 RunLoop 里注冊了兩個 Observer,其回調(diào)都是 _wrapRunLoopWithAutoreleasePoolHandler()棕洋。
第一個 Observer 監(jiān)視的事件是 Entry(即將進(jìn)入Loop)挡闰,其回調(diào)內(nèi)會調(diào)用 _objc_autoreleasePoolPush() 創(chuàng)建自動釋放池。其 order 是 -2147483647掰盘,優(yōu)先級最高摄悯,保證創(chuàng)建釋放池發(fā)生在其他所有回調(diào)之前。
第二個 Observer 監(jiān)視了兩個事件: BeforeWaiting(準(zhǔn)備進(jìn)入休眠) 時調(diào)用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池并創(chuàng)建新池愧捕;Exit(即將退出Loop) 時調(diào)用 _objc_autoreleasePoolPop() 來釋放自動釋放池奢驯。這個 Observer 的 order 是 2147483647,優(yōu)先級最低次绘,保證其釋放池子發(fā)生在其他所有回調(diào)之后瘪阁。
在主線程執(zhí)行的代碼,通常是寫在諸如事件回調(diào)邮偎、Timer回調(diào)內(nèi)的管跺。這些回調(diào)會被 RunLoop 創(chuàng)建好的 AutoreleasePool 環(huán)繞著,所以不會出現(xiàn)內(nèi)存泄漏禾进,開發(fā)者也不必顯示創(chuàng)建 Pool 了豁跑。
5.GCD 在Runloop中的使用?
- GCD由 子線程 返回到 主線程,只有在這種情況下才會觸發(fā) RunLoop泻云。會觸發(fā) RunLoop 的 Source 1 事件艇拍。
6.AFNetworking 中如何運(yùn)用 Runloop?
-
AFURLConnectionOperation 這個類是基于 NSURLConnection 構(gòu)建的,其希望能在后臺線程接收 Delegate 回調(diào)宠纯。為此 AFNetworking 單獨(dú)創(chuàng)建了一個線程卸夕,并在這個線程中啟動了一個 RunLoop:
+ (void)networkRequestThreadEntryPoint:(id)__unused object { @autoreleasepool { [[NSThread currentThread] setName:@"AFNetworking"]; NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; [runLoop run]; } } + (NSThread *)networkRequestThread { static NSThread *_networkRequestThread = nil; static dispatch_once_t oncePredicate; dispatch_once(&oncePredicate, ^{ _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil]; [_networkRequestThread start]; }); return _networkRequestThread; }
-
RunLoop 啟動前內(nèi)部必須要有至少一個 Timer/Observer/Source,所以 AFNetworking 在 [runLoop run] 之前先創(chuàng)建了一個新的 NSMachPort 添加進(jìn)去了婆瓜。通常情況下快集,調(diào)用者需要持有這個 NSMachPort (mach_port) 并在外部線程通過這個 port 發(fā)送消息到 loop 內(nèi);但此處添加 port 只是為了讓 RunLoop 不至于退出勃救,并沒有用于實(shí)際的發(fā)送消息。
- (void)start { [self.lock lock]; if ([self isCancelled]) { [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; } else if ([self isReady]) { self.state = AFOperationExecutingState; [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; } [self.lock unlock]; }
當(dāng)需要這個后臺線程執(zhí)行任務(wù)時治力,AFNetworking 通過調(diào)用 [NSObject performSelector:onThread:..] 將這個任務(wù)扔到了后臺線程的 RunLoop 中蒙秒。
7.PerformSelector 的實(shí)現(xiàn)原理?
當(dāng)調(diào)用 NSObject 的 performSelecter:afterDelay: 后宵统,實(shí)際上其內(nèi)部會創(chuàng)建一個 Timer 并添加到當(dāng)前線程的 RunLoop 中晕讲。所以如果當(dāng)前線程沒有 RunLoop覆获,則這個方法會失效。
當(dāng)調(diào)用 performSelector:onThread: 時瓢省,實(shí)際上其會創(chuàng)建一個 Timer 加到對應(yīng)的線程去弄息,同樣的,如果對應(yīng)線程沒有 RunLoop 該方法也會失效勤婚。
8.PerformSelector:afterDelay:這個方法在子線程中是否起作用摹量?
- 不起作用,子線程默認(rèn)沒有 Runloop馒胆,也就沒有 Timer缨称。可以使用 GCD的dispatch_after來實(shí)現(xiàn)
9.事件響應(yīng)的過程祝迂?
蘋果注冊了一個 Source1 (基于 mach port 的) 用來接收系統(tǒng)事件睦尽,其回調(diào)函數(shù)為 __IOHIDEventSystemClientQueueCallback()。
當(dāng)一個硬件事件(觸摸/鎖屏/搖晃等)發(fā)生后型雳,首先由 IOKit.framework 生成一個 IOHIDEvent 事件并由 SpringBoard 接收当凡。這個過程的詳細(xì)情況可以參考這里。SpringBoard 只接收按鍵(鎖屏/靜音等)纠俭,觸摸沿量,加速,接近傳感器等幾種 Event柑晒,隨后用 mach port 轉(zhuǎn)發(fā)給需要的 App 進(jìn)程欧瘪。隨后蘋果注冊的那個 Source1 就會觸發(fā)回調(diào),并調(diào)用 _UIApplicationHandleEventQueue() 進(jìn)行應(yīng)用內(nèi)部的分發(fā)匙赞。
_UIApplicationHandleEventQueue() 會把 IOHIDEvent 處理并包裝成 UIEvent 進(jìn)行處理或分發(fā)佛掖,其中包括識別 UIGesture/處理屏幕旋轉(zhuǎn)/發(fā)送給 UIWindow 等。通常事件比如 UIButton 點(diǎn)擊涌庭、touchesBegin/Move/End/Cancel 事件都是在這個回調(diào)中完成的芥被。
10.手勢識別的過程?
當(dāng) _UIApplicationHandleEventQueue() 識別了一個手勢時坐榆,其首先會調(diào)用 Cancel 將當(dāng)前的 touchesBegin/Move/End 系列回調(diào)打斷拴魄。隨后系統(tǒng)將對應(yīng)的 UIGestureRecognizer 標(biāo)記為待處理。
蘋果注冊了一個 Observer 監(jiān)測 BeforeWaiting (Loop即將進(jìn)入休眠) 事件席镀,這個 Observer 的回調(diào)函數(shù)是 _UIGestureRecognizerUpdateObserver()匹中,其內(nèi)部會獲取所有剛被標(biāo)記為待處理的 GestureRecognizer,并執(zhí)行GestureRecognizer 的回調(diào)豪诲。
當(dāng)有 UIGestureRecognizer 的變化(創(chuàng)建/銷毀/狀態(tài)改變)時顶捷,這個回調(diào)都會進(jìn)行相應(yīng)處理。
11.CADispalyTimer和Timer哪個更精確[圖片上傳失敗...(image-eb7a24-1646208864544)]
CADisplayLink 更精確
iOS設(shè)備的屏幕刷新頻率是固定的屎篱,CADisplayLink在正常情況下會在每次刷新結(jié)束都被調(diào)用服赎,精確度相當(dāng)高葵蒂。
NSTimer的精確度就顯得低了點(diǎn),比如NSTimer的觸發(fā)時間到的時候重虑,runloop如果在阻塞狀態(tài)践付,觸發(fā)時間就會推遲到下一個runloop周期。并且 NSTimer新增了tolerance屬性缺厉,讓用戶可以設(shè)置可以容忍的觸發(fā)的時間的延遲范圍永高。
CADisplayLink使用場合相對專一,適合做UI的不停重繪芽死,比如自定義動畫引擎或者視頻播放的渲染乏梁。NSTimer的使用范圍要廣泛的多,各種需要單次或者循環(huán)定時處理的任務(wù)都可以使用关贵。在UI相關(guān)的動畫或者顯示內(nèi)容使用 CADisplayLink比起用NSTimer的好處就是我們不需要在格外關(guān)心屏幕的刷新頻率了遇骑,因?yàn)樗旧砭褪歉聊凰⑿峦降摹?/p>
項(xiàng)目架構(gòu)
1.MVC、MVP揖曾、MVVM模式
#MVC(Model落萎、View、Controller)
MVC是比較直觀的架構(gòu)模式炭剪,最核心的就是通過Controller層來進(jìn)行調(diào)控练链,首先看一下官方提供的MVC示意圖:
[圖片上傳失敗...(image-6f035d-1679319743058)]
Model和View永遠(yuǎn)不能相互通信,只能通過Controller傳遞
Controller可以直接與Model對話(讀寫調(diào)用Model)奴拦,Model通過NOtification和KVO機(jī)制與Controller間接通信
Controller可以直接與View對話媒鼓,通過IBoutlet直接操作View,IBoutlet直接對應(yīng)View的控件(例如創(chuàng)建一個Button:需聲明一個 IBOutlet UIButton * btn)错妖,View通過action向Controller報告時間的發(fā)生(用戶點(diǎn)擊了按鈕)绿鸣。Controller是View的直接數(shù)據(jù)源
優(yōu)點(diǎn):對于混亂的項(xiàng)目組織方式,有了一個明確的組織方式暂氯。通過Controller來掌控全局潮模,同時將View展示和Model的變化分開
缺點(diǎn):愈發(fā)笨重的Controller,隨著業(yè)務(wù)邏輯的增加痴施,大量的代碼放進(jìn)Controller擎厢,導(dǎo)致Controller越來越臃腫,堆積成千上萬行代碼辣吃,后期維護(hù)起來費(fèi)時費(fèi)力
#MVP(Model动遭、View、Presenter)
MVP模式是MVC模式的一個演化版本神得,其中Model與MVC模式中Model層沒有太大區(qū)別厘惦,主要提供數(shù)據(jù)存儲功能,一般都是用來封裝網(wǎng)絡(luò)獲取的json數(shù)據(jù)循头;View與MVC中的View層有一些差別绵估,MVP中的View層可以是viewController、view等控件卡骂;Presenter層則是作為Model和View的中介国裳,從Model層獲取數(shù)據(jù)之后傳給View。
[圖片上傳失敗...(image-5d6ab9-1679319743058)]
從上圖可以看出全跨,從MVC模式中增加了Presenter層缝左,將UIViewController中復(fù)雜的業(yè)務(wù)邏輯、網(wǎng)絡(luò)請求等剝離出來浓若。
優(yōu)點(diǎn) 模型和視圖完全分離渺杉,可以做到修改視圖而不影響模型;更高效的使用模型挪钓,View不依賴Model是越,可以說VIew能做到對業(yè)務(wù)邏輯完全分離
缺點(diǎn) Presenter中除了處理業(yè)務(wù)邏輯以外,還要處理View-Model兩層的協(xié)調(diào)碌上,也會導(dǎo)致Presenter層的臃腫
#MVVM(Model倚评、Controller/View、ViewModel)
在MVVM中馏予,view和ViewCOntroller聯(lián)系在一起天梧,我們把它們視為一個組件,view和ViewController都不能直接引用model霞丧,而是引用是視圖模型即ViewModel呢岗。 viewModel是一個用來放置用戶輸入驗(yàn)證邏輯、視圖顯示邏輯蛹尝、網(wǎng)絡(luò)請求等業(yè)務(wù)邏輯的地方后豫,這樣的設(shè)計模式,會輕微增加代碼量箩言,但是會減少代碼的復(fù)雜性
優(yōu)點(diǎn) VIew可以獨(dú)立于Model的變化和修改硬贯,一個ViewModel可以綁定到不同的View上,降低耦合陨收,增加重用
缺點(diǎn) 過于簡單的項(xiàng)目不適用饭豹、大型的項(xiàng)目視圖狀態(tài)較多時構(gòu)建和維護(hù)成本太大
合理的運(yùn)用架構(gòu)模式有利于項(xiàng)目、團(tuán)隊開發(fā)工作务漩,但是到底選擇哪個設(shè)計模式拄衰,哪種設(shè)計模式更好,就像本文開頭所說饵骨,不同的設(shè)計模式翘悉,只是讓不同的場景有了更多的選擇方案。根據(jù)項(xiàng)目場景和開發(fā)需求居触,選擇最合適的解決方案妖混。
2.關(guān)于RAC你有怎樣運(yùn)用到解決不同API依賴關(guān)系
-
信號的依賴:使用場景是當(dāng)信號A執(zhí)行完才會執(zhí)行信號B,和請求的依賴很類似,例如請求A請求完畢才執(zhí)行請求B,我們需要注意信號A必須要執(zhí)行發(fā)送完成信號,否則信號B無法執(zhí)行
//這相當(dāng)于網(wǎng)絡(luò)請求中的依賴,必須先執(zhí)行完信號A才會執(zhí)行信號B //經(jīng)常用作一個請求執(zhí)行完畢后,才會執(zhí)行另一個請求 //注意信號A必須要執(zhí)行發(fā)送完成信號,否則信號B無法執(zhí)行 RACSignal * concatSignal = [self.signalA concat:self.signalB] //這里我們是對這個拼接信號進(jìn)行訂閱 [concatSignal subscribeNext:^(id x) { NSLog(@"%@",x); }];
3.@weakify和我們宏定義的WeakSelf有什么區(qū)別老赤?
@weakify 可以多參數(shù)使用
4.微服務(wù)架構(gòu)設(shè)想。
微服務(wù)架構(gòu)具有以下優(yōu)勢:
-
1.靈活和獨(dú)立的可擴(kuò)展性
靈活擴(kuò)展是微服務(wù)架構(gòu)的主要優(yōu)勢之一制市。與單片架構(gòu)不同抬旺,每個模塊都可以水平擴(kuò)展并獨(dú)立于其他模塊。因此祥楣,微服務(wù)架構(gòu)非常適合大型項(xiàng)目开财。
-
2.獨(dú)立技術(shù)堆棧
在微服務(wù)架構(gòu)中,軟件工程師有機(jī)會使用各種工具和技術(shù)構(gòu)建APP误褪。代碼可以用不同的編程語言編寫责鳍,這為APP開發(fā)過程增加了更多的靈活性。
-
3.更好的故障隔離
如果一個服務(wù)失敗兽间,它不會影響其他服務(wù)的功能历葛。與其他體系結(jié)構(gòu)樣式相比,在微服務(wù)中嘀略,系統(tǒng)繼續(xù)工作啃洋,單片模式下的問題會影響整個APP。
-
4.易于部署和集成
雖然即使是小代碼更改的情況下屎鳍,開發(fā)人員也必須再次部署APP宏娄,但在微服務(wù)架構(gòu)中,部署變得更快更輕松逮壁。
由于所有服務(wù)都是圍繞單一業(yè)務(wù)流程構(gòu)建的孵坚,因此程序員不必修改和重新部署整個APP,只需要您需要的區(qū)域窥淆。因此卖宠,改進(jìn)產(chǎn)品也比較簡單。
微服務(wù)可以通過全自動部署機(jī)制獨(dú)立部署忧饭。此外扛伍,通過使用開源持續(xù)集成工具,開發(fā)人員大大簡化了與第三方服務(wù)的集成词裤。
-
5.容易理解
微服務(wù)體系結(jié)構(gòu)的另一個優(yōu)點(diǎn)是很容易理解系統(tǒng)是如何工作的以及它是如何開發(fā)的刺洒。當(dāng)一個新的團(tuán)隊成員來到這個項(xiàng)目并且必須快速鉆研它時,這特別有用吼砂。
那么在iOS中如何借鑒這種思想去構(gòu)建我們的App呢逆航?是需要我們開發(fā)者自己去不斷探索的
設(shè)計模式
1.iOS有哪些常見的設(shè)計模式?
單例模式:單例保證了應(yīng)用程序的生命周期內(nèi)僅有一個該類的實(shí)例對象,而且易于外界訪問.在ios sdk中,UIApplication, NSBundle, NSNotificationCenter, NSFileManager, NSUserDefault, NSURLCache等都是單例.
委托模式:委托Delegate是協(xié)議的一種,通過@protocol方式實(shí)現(xiàn),常見的有tableView渔肩,textField等因俐。
觀察者模式:觀察者模式定義了一種一對多的依賴關(guān)系槐瑞,讓多個觀察者對象同時監(jiān)聽某一個主題對象九默。在iOS中,觀察者模式的具體實(shí)現(xiàn)有兩種: 通知機(jī)制(notification)和KVO機(jī)制(Key-value Observing)
2.單例會有什么弊端?
-
主要優(yōu)點(diǎn):
1搀绣、提供了對唯一實(shí)例的受控訪問慌植。
2陕悬、由于在系統(tǒng)內(nèi)存中只存在一個對象状植,因此可以節(jié)約系統(tǒng)資源优炬,對于一些需要頻繁創(chuàng)建和銷毀的對象單例模式無疑可以提高系統(tǒng)的性能。
3境蔼、允許可變數(shù)目的實(shí)例。
-
主要缺點(diǎn):
1伺通、由于單利模式中沒有抽象層箍土,因此單例類的擴(kuò)展有很大的困難。
2罐监、單例類的職責(zé)過重吴藻,在一定程度上違背了“單一職責(zé)原則”。
3弓柱、濫用單例將帶來一些負(fù)面問題沟堡,如為了節(jié)省資源將數(shù)據(jù)庫連接池對象設(shè)計為的單例類,可能會導(dǎo)致共享連接池對象的程序過多而出現(xiàn)連接池溢出矢空;如果實(shí)例化的對象長時間不被利用航罗,系統(tǒng)會認(rèn)為是垃圾而被回收,這將導(dǎo)致對象狀態(tài)的丟失屁药。
3.編程中的六大設(shè)計原則粥血?
-
1.單一職責(zé)原則
通俗地講就是一個類只做一件事
CALayer:動畫和視圖的顯示。
UIView:只負(fù)責(zé)事件傳遞酿箭、事件響應(yīng)复亏。
-
2.開閉原則
對修改關(guān)閉,對擴(kuò)展開放缭嫡。 要考慮到后續(xù)的擴(kuò)展性缔御,而不是在原有的基礎(chǔ)上來回修改
-
3.接口隔離原則
使用多個專門的協(xié)議、而不是一個龐大臃腫的協(xié)議妇蛀,如 UITableviewDelegate + UITableViewDataSource
-
4.依賴倒置原則
抽象不應(yīng)該依賴于具體實(shí)現(xiàn)耕突、具體實(shí)現(xiàn)可以依賴于抽象。 調(diào)用接口感覺不到內(nèi)部是如何操作的
-
5.里氏替換原則
父類可以被子類無縫替換评架,且原有的功能不受任何影響 如:KVO
-
6.迪米特法則
一個對象應(yīng)當(dāng)對其他對象盡可能少的了解有勾,實(shí)現(xiàn)高聚合、低耦合
組件化
1.組件化有什么好處古程?
業(yè)務(wù)分層蔼卡、解耦,使代碼變得可維護(hù);
有效的拆分雇逞、組織日益龐大的工程代碼荤懂,使工程目錄變得可維護(hù);
便于各業(yè)務(wù)功能拆分塘砸、抽離节仿,實(shí)現(xiàn)真正的功能復(fù)用;
業(yè)務(wù)隔離掉蔬,跨團(tuán)隊開發(fā)代碼控制和版本風(fēng)險控制的實(shí)現(xiàn)廊宪;
模塊化對代碼的封裝性、合理性都有一定的要求女轿,提升開發(fā)同學(xué)的設(shè)計能力箭启;
在維護(hù)好各級組件的情況下,隨意組合滿足不同客戶需求蛉迹;(只需要將之前的多個業(yè)務(wù)組件模塊在新的主App中進(jìn)行組裝即可快速迭代出下一個全新App)
2.你是如何組件化解耦的傅寡?
-
分層
基礎(chǔ)功能組件:按功能分庫,不涉及產(chǎn)品業(yè)務(wù)需求北救,跟庫Library類似荐操,通過良好的接口拱上層業(yè)務(wù)組件調(diào)用;不寫入產(chǎn)品定制邏輯珍策,通過擴(kuò)展接口完成定制托启;
基礎(chǔ)UI組件:各個業(yè)務(wù)模塊依賴使用,但需要保持好定制擴(kuò)展的設(shè)計
業(yè)務(wù)組件:業(yè)務(wù)功能間相對獨(dú)立攘宙,相互間沒有Model共享的依賴驾中;業(yè)務(wù)之間的頁面調(diào)用只能通過UIBus進(jìn)行跳轉(zhuǎn);業(yè)務(wù)之間的邏輯Action調(diào)用只能通過服務(wù)提供模聋;
中間件:target-action肩民,url-block,protocol-class
3.為什么CTMediator方案優(yōu)于基于Router的方案链方?
Router的缺點(diǎn):
在組件化的實(shí)施過程中持痰,注冊URL并不是充分必要條件。組件是不需要向組件管理器注冊URL的祟蚀,注冊了URL之后工窍,會造成不必要的內(nèi)存常駐。注冊URL的目的其實(shí)是一個服務(wù)發(fā)現(xiàn)的過程前酿,在iOS領(lǐng)域中患雏,服務(wù)發(fā)現(xiàn)的方式是不需要通過主動注冊的,使用runtime就可以了罢维。另外淹仑,注冊部分的代碼的維護(hù)是一個相對麻煩的事情,每一次支持新調(diào)用時,都要去維護(hù)一次注冊列表匀借。如果有調(diào)用被棄用了颜阐,是經(jīng)常會忘記刪項(xiàng)目的。runtime由于不存在注冊過程吓肋,那就也不會產(chǎn)生維護(hù)的操作凳怨,維護(hù)成本就降低了。 由于通過runtime做到了服務(wù)的自動發(fā)現(xiàn)是鬼,拓展調(diào)用接口的任務(wù)就僅在于各自的模塊肤舞,任何一次新接口添加,新業(yè)務(wù)添加均蜜,都不必去主工程做操作李剖,十分透明。
在iOS領(lǐng)域里兆龙,一定是組件化的中間件為openURL提供服務(wù),而不是openURL方式為組件化提供服務(wù)敲董。如果在給App實(shí)施組件化方案的過程中是基于openURL的方案的話紫皇,有一個致命缺陷:非常規(guī)對象(不能被字符串化到URL中的對象,例如UIImage)無法參與本地組件間調(diào)度腋寨。 在本地調(diào)用中使用URL的方式其實(shí)是不必要的聪铺,如果業(yè)務(wù)工程師在本地間調(diào)度時需要給出URL,那么就不可避免要提供params萄窜,在調(diào)用時要提供哪些params是業(yè)務(wù)工程師很容易懵逼的地方铃剔。
為了支持傳遞非常規(guī)參數(shù),蘑菇街的方案采用了protocol查刻,這個會侵入業(yè)務(wù)键兜。由于業(yè)務(wù)中的某個對象需要被調(diào)用,因此必須要符合某個可被調(diào)用的protocol穗泵,然而這個protocol又不存在于當(dāng)前業(yè)務(wù)領(lǐng)域普气,于是當(dāng)前業(yè)務(wù)就不得不依賴public Protocol。這對于將來的業(yè)務(wù)遷移是有非常大的影響的佃延。
CTMediator的優(yōu)點(diǎn):
調(diào)用時现诀,區(qū)分了本地應(yīng)用調(diào)用和遠(yuǎn)程應(yīng)用調(diào)用。本地應(yīng)用調(diào)用為遠(yuǎn)程應(yīng)用調(diào)用提供服務(wù)履肃。
組件僅通過Action暴露可調(diào)用接口仔沿,模塊與模塊之間的接口被固化在了Target-Action這一層,避免了實(shí)施組件化的改造過程中尺棋,對Business的侵入封锉,同時也提高了組件化接口的可維護(hù)性。
方便傳遞各種類型的參數(shù)。
4.基于CTMediator的組件化方案烘浦,有哪些核心組成抖坪?
CTMediator中間件:集成就可以了
模塊Target_%@:模塊的實(shí)現(xiàn)及提供對外的方法調(diào)用Action_methodName,需要傳參數(shù)時闷叉,都統(tǒng)一以NSDictionary*的形式傳入擦俐。
CTMediator+%@擴(kuò)展:擴(kuò)展里聲明了模塊業(yè)務(wù)的對外接口,參數(shù)明確握侧,這樣外部調(diào)用者可以很容易理解如何調(diào)用接口蚯瞧。
調(diào)試技巧
1.LLDB常用的調(diào)試命令?
po:print object的縮寫品擎,表示顯示對象的文本描述埋合,如果對象不存在則打印nil。
p:可以用來打印基本數(shù)據(jù)類型萄传。
call:執(zhí)行一段代碼 如:call NSLog(@"%@", @"yang")
expr:動態(tài)執(zhí)行指定表達(dá)式
bt:打印當(dāng)前線程堆棧信息 (bt all 打印所有線程堆棧信息)
image:常用來尋找棧地址對應(yīng)代碼位置 如:image lookup --address 0xxxx
2.斷點(diǎn)調(diào)試
-
條件斷點(diǎn)
打上斷點(diǎn)之后甚颂,對斷點(diǎn)進(jìn)行編輯,設(shè)置相應(yīng)過濾條件秀菱。下面簡單的介紹一下條件設(shè)置:
Condition:返回一個布爾值振诬,當(dāng)布爾值為真觸發(fā)斷點(diǎn),一般里面我們可以寫一個表達(dá)式衍菱。
Ignore:忽略前N次斷點(diǎn)赶么,到N+1次再觸發(fā)斷點(diǎn)。
Action:斷點(diǎn)觸發(fā)事件脊串,分為六種:
AppleScript:執(zhí)行腳本辫呻。
Capture GPU Frame:用于OpenGL ES調(diào)試,捕獲斷點(diǎn)處GPU當(dāng)前繪制幀琼锋。
Debugger Command:和控制臺中輸入LLDB調(diào)試命令一致放闺。
Log Message:輸出自定義格式信息至控制臺。
Shell Command:接收命令文件及相應(yīng)參數(shù)列表缕坎,Shell Command是異步執(zhí)行的雄人,只有勾選“Wait until done”才會等待Shell命令執(zhí)行完在執(zhí)行調(diào)試。
Sound:斷點(diǎn)觸發(fā)時播放聲音念赶。
Options(Automatically continue after evaluating actions選項(xiàng)):選中后础钠,表示斷點(diǎn)不會終止程序的運(yùn)行。
-
異常斷點(diǎn)
異常斷點(diǎn)可以快速定位不滿足特定條件的異常叉谜,比如常見的數(shù)組越界旗吁,這時候很難通過異常信息定位到錯誤所在位置。這個時候異常斷點(diǎn)就可以發(fā)揮作用了停局。
Exception:可以選擇拋出異常對象類型:OC或C++很钓。
Break:選擇斷點(diǎn)接收的拋出異常來源是Throw還是Catch語句香府。
-
符號斷點(diǎn)
符號斷點(diǎn)的創(chuàng)建方式和異常斷點(diǎn)一樣一樣的,在符號斷點(diǎn)中可以指定要中斷執(zhí)行的方法:
Symbol:[類名 方法名]可以執(zhí)行到指定類的指定方法中開始斷點(diǎn)码倦。
3.iOS 常見的崩潰類型有哪些企孩?
unrecognized selector crash
KVO crash
NSNotification crash
NSTimer crash
Container crash
NSString crash
Bad Access crash (野指針)
UI not on Main Thread Crash
性能優(yōu)化
1.造成tableView卡頓的原因有哪些?
-
1.最常用的就是cell的重用袁稽, 注冊重用標(biāo)識符
如果不重用cell時勿璃,每當(dāng)一個cell顯示到屏幕上時,就會重新創(chuàng)建一個新的cell
如果有很多數(shù)據(jù)的時候推汽,就會堆積很多cell补疑。
如果重用cell,為cell創(chuàng)建一個ID歹撒,每當(dāng)需要顯示cell 的時候莲组,都會先去緩沖池中尋找可循環(huán)利用的cell,如果沒有再重新創(chuàng)建cell
-
2.避免cell的重新布局
cell的布局填充等操作 比較耗時暖夭,一般創(chuàng)建時就布局好
如可以將cell單獨(dú)放到一個自定義類锹杈,初始化時就布局好
-
3.提前計算并緩存cell的屬性及內(nèi)容
當(dāng)我們創(chuàng)建cell的數(shù)據(jù)源方法時,編譯器并不是先創(chuàng)建cell 再定cell的高度
而是先根據(jù)內(nèi)容一次確定每一個cell的高度迈着,高度確定后竭望,再創(chuàng)建要顯示的cell,滾動時寥假,每當(dāng)cell進(jìn)入憑虛都會計算高度市框,提前估算高度告訴編譯器霞扬,編譯器知道高度后糕韧,緊接著就會創(chuàng)建cell,這時再調(diào)用高度的具體計算方法喻圃,這樣可以方式浪費(fèi)時間去計算顯示以外的cell
-
4.減少cell中控件的數(shù)量
盡量使cell得布局大致相同萤彩,不同風(fēng)格的cell可以使用不用的重用標(biāo)識符,初始化時添加控件斧拍,
不適用的可以先隱藏
-
5.不要使用ClearColor雀扶,無背景色,透明度也不要設(shè)置為0
渲染耗時比較長
-
6.使用局部更新
如果只是更新某組的話肆汹,使用reloadSection進(jìn)行局部更
7.加載網(wǎng)絡(luò)數(shù)據(jù)愚墓,下載圖片,使用異步加載昂勉,并緩存
8.少使用addView 給cell動態(tài)添加view
9.按需加載cell浪册,cell滾動很快時,只加載范圍內(nèi)的cell
10.不要實(shí)現(xiàn)無用的代理方法岗照,tableView只遵守兩個協(xié)議
11.緩存行高:estimatedHeightForRow不能和HeightForRow里面的layoutIfNeed同時存在村象,這兩者同時存在才會出現(xiàn)“竄動”的bug笆环。所以我的建議是:只要是固定行高就寫預(yù)估行高來減少行高調(diào)用次數(shù)提升性能。如果是動態(tài)行高就不要寫預(yù)估方法了厚者,用一個行高的緩存字典來減少代碼的調(diào)用次數(shù)即可
12.不要做多余的繪制工作躁劣。在實(shí)現(xiàn)drawRect:的時候,它的rect參數(shù)就是需要繪制的區(qū)域库菲,這個區(qū)域之外的不需要進(jìn)行繪制账忘。例如上例中,就可以用CGRectIntersectsRect蝙昙、CGRectIntersection或CGRectContainsRect判斷是否需要繪制image和text闪萄,然后再調(diào)用繪制方法。
13.預(yù)渲染圖像奇颠。當(dāng)新的圖像出現(xiàn)時败去,仍然會有短暫的停頓現(xiàn)象。解決的辦法就是在bitmap context里先將其畫一遍烈拒,導(dǎo)出成UIImage對象圆裕,然后再繪制到屏幕;
14.使用正確的數(shù)據(jù)結(jié)構(gòu)來存儲數(shù)據(jù)荆几。
2.如何提升 tableview 的流暢度吓妆?
-
本質(zhì)上是降低 CPU、GPU 的工作吨铸,從這兩個大的方面去提升性能行拢。
CPU:對象的創(chuàng)建和銷毀、對象屬性的調(diào)整诞吱、布局計算舟奠、文本的計算和排版、圖片的格式轉(zhuǎn)換和解碼房维、圖像的繪制
GPU:紋理的渲染
-
卡頓優(yōu)化在 CPU 層面
盡量用輕量級的對象沼瘫,比如用不到事件處理的地方,可以考慮使用 CALayer 取代 UIView
不要頻繁地調(diào)用 UIView 的相關(guān)屬性咙俩,比如 frame耿戚、bounds、transform 等屬性阿趁,盡量減少不必要的修改
盡量提前計算好布局膜蛔,在有需要時一次性調(diào)整對應(yīng)的屬性,不要多次修改屬性
Autolayout 會比直接設(shè)置 frame 消耗更多的 CPU 資源
圖片的 size 最好剛好跟 UIImageView 的 size 保持一致
控制一下線程的最大并發(fā)數(shù)量
盡量把耗時的操作放到子線程
文本處理(尺寸計算脖阵、繪制)
圖片處理(解碼皂股、繪制)
-
卡頓優(yōu)化在 GPU層面
盡量避免短時間內(nèi)大量圖片的顯示,盡可能將多張圖片合成一張進(jìn)行顯示
GPU能處理的最大紋理尺寸是 4096x4096独撇,一旦超過這個尺寸屑墨,就會占用 CPU 資源進(jìn)行處理躁锁,所以紋理盡量不要超過這個尺寸
盡量減少視圖數(shù)量和層次
減少透明的視圖(alpha<1),不透明的就設(shè)置 opaque 為 YES
盡量避免出現(xiàn)離屏渲染
-
iOS 保持界面流暢的技巧
1.預(yù)排版卵史,提前計算
在接收到服務(wù)端返回的數(shù)據(jù)后战转,盡量將 CoreText 排版的結(jié)果、單個控件的高度以躯、cell 整體的高度提前計算好槐秧,將其存儲在模型的屬性中。需要使用時忧设,直接從模型中往外取刁标,避免了計算的過程。
盡量少用 UILabel址晕,可以使用 CALayer 膀懈。避免使用 AutoLayout 的自動布局技術(shù),采取純代碼的方式
2.預(yù)渲染谨垃,提前繪制
例如圓形的圖標(biāo)可以提前在启搂,在接收到網(wǎng)絡(luò)返回數(shù)據(jù)時,在后臺線程進(jìn)行處理刘陶,直接存儲在模型數(shù)據(jù)里胳赌,回到主線程后直接調(diào)用就可以了
避免使用 CALayer 的 Border、corner匙隔、shadow疑苫、mask 等技術(shù),這些都會觸發(fā)離屏渲染纷责。
3.異步繪制
4.全局并發(fā)線程
5.高效的圖片異步加載
3.APP啟動時間應(yīng)從哪些方面優(yōu)化捍掺?
App啟動時間可以通過xcode提供的工具來度量,在Xcode的Product->Scheme-->Edit Scheme->Run->Auguments中碰逸,將環(huán)境變量DYLD_PRINT_STATISTICS設(shè)為YES乡小,優(yōu)化需以下方面入手
-
dylib loading time
核心思想是減少dylibs的引用
合并現(xiàn)有的dylibs(最好是6個以內(nèi))
使用靜態(tài)庫
-
rebase/binding time
核心思想是減少DATA塊內(nèi)的指針
減少Object C元數(shù)據(jù)量阔加,減少Objc類數(shù)量饵史,減少實(shí)例變量和函數(shù)(與面向?qū)ο笤O(shè)計思想沖突)
減少c++虛函數(shù)
多使用Swift結(jié)構(gòu)體(推薦使用swift)
-
ObjC setup time
核心思想同上,這部分內(nèi)容基本上在上一階段優(yōu)化過后就不會太過耗時
initializer time
-
使用initialize替代load方法
減少使用c/c++的attribute((constructor))胜榔;推薦使用dispatch_once() pthread_once() std:once()等方法
推薦使用swift
不要在初始化中調(diào)用dlopen()方法胳喷,因?yàn)榧虞d過程是單線程,無鎖夭织,如果調(diào)用dlopen則會變成多線程吭露,會開啟鎖的消耗,同時有可能死鎖
不要在初始化中創(chuàng)建線程
4.如何降低APP包的大小
降低包大小需要從兩方面著手
-
可執(zhí)行文件
編譯器優(yōu)化:Strip Linked Product尊惰、Make Strings Read-Only讲竿、Symbols Hidden by Default 設(shè)置為 YES泥兰,去掉異常支持,Enable C++ Exceptions题禀、Enable Objective-C Exceptions 設(shè)置為 NO鞋诗, Other C Flags 添加 -fno-exceptions 利用 AppCode 檢測未使用的代碼:菜單欄 -> Code -> Inspect Code
編寫LLVM插件檢測出重復(fù)代碼、未被調(diào)用的代碼
-
資源(圖片迈嘹、音頻削彬、視頻 等)
優(yōu)化的方式可以對資源進(jìn)行無損的壓縮
5.如何檢測離屏渲染與優(yōu)化
檢測,通過勾選Xcode的Debug->View Debugging-->Rendering->Run->Color Offscreen-Rendered Yellow項(xiàng)秀仲。
優(yōu)化融痛,如陰影,在繪制時添加陰影的路徑
6.怎么檢測圖層混合[圖片上傳失敗...(image-f70042-1646209378692)]
1神僵、模擬器debug中color blended layers紅色區(qū)域表示圖層發(fā)生了混合
2雁刷、Instrument-選中Core Animation-勾選Color Blended Layers
避免圖層混合:
確保控件的opaque屬性設(shè)置為true保礼,確保backgroundColor和父視圖顏色一致且不透明
如無特殊需要安券,不要設(shè)置低于1的alpha值
確保UIImage沒有alpha通道
UILabel圖層混合解決方法:
iOS8以后設(shè)置背景色為非透明色并且設(shè)置label.layer.masksToBounds=YES讓label只會渲染她的實(shí)際size區(qū)域,就能解決UILabel的圖層混合問題
iOS8 之前只要設(shè)置背景色為非透明的就行
為什么設(shè)置了背景色但是在iOS8上仍然出現(xiàn)了圖層混合呢氓英?
UILabel在iOS8前后的變化侯勉,在iOS8以前,UILabel使用的是CALayer作為底圖層铝阐,而在iOS8開始址貌,UILabel的底圖層變成了_UILabelLayer,繪制文本也有所改變徘键。在背景色的四周多了一圈透明的邊练对,而這一圈透明的邊明顯超出了圖層的矩形區(qū)域,設(shè)置圖層的masksToBounds為YES時吹害,圖層將會沿著Bounds進(jìn)行裁剪 圖層混合問題解決了
7.日常如何檢查內(nèi)存泄露螟凭?
-
目前我知道的方式有以下幾種
Memory Leaks
Alloctions
Analyse
Debug Memory Graph
MLeaksFinder
-
泄露的內(nèi)存主要有以下兩種:
Laek Memory 這種是忘記 Release 操作所泄露的內(nèi)存。
Abandon Memory 這種是循環(huán)引用它呀,無法釋放掉的內(nèi)存螺男。
源碼理解
1.YYKit
YYKit 是一組龐大、功能豐富的 iOS 組件纵穿。
我們需要從組件架構(gòu)下隧,源碼,設(shè)計思路谓媒,解決問題的方案策略等多方面去學(xué)習(xí)淆院。
2.SDWebImage加載圖片過程
0、首先顯示占位圖
1句惯、在webimagecache中尋找圖片對應(yīng)的緩存土辩,它是以url為數(shù)據(jù)索引先在內(nèi)存中查找是否有緩存支救;
2、如果沒有緩存拷淘,就通過md5處理過的key來在磁盤中查找對應(yīng)的數(shù)據(jù)搂妻,如果找到就會把磁盤中的數(shù)據(jù)加到內(nèi)存中,并顯示出來辕棚;
3欲主、如果內(nèi)存和磁盤中都沒有找到,就會向遠(yuǎn)程服務(wù)器發(fā)送請求逝嚎,開始下載圖片扁瓢;
4、下載完的圖片加入緩存中补君,并寫入到磁盤中引几;
5、整個獲取圖片的過程是在子線程中進(jìn)行挽铁,在主線程中顯示伟桅。
4.AFNetworking 底層原理分析
AFNetworking是封裝的NSURLSession的網(wǎng)絡(luò)請求,由五個模塊組成:分別由NSURLSession,Security,Reachability,Serialization,UIKit五部分組成
NSURLSession:網(wǎng)絡(luò)通信模塊(核心模塊) 對應(yīng) AFNetworking中的 AFURLSessionManager和對HTTP協(xié)議進(jìn)行特化處理的AFHTTPSessionManager,AFHTTPSessionManager是繼承于AFURLSessionmanager的
Security:網(wǎng)絡(luò)通訊安全策略模塊 對應(yīng) AFSecurityPolicy
Reachability:網(wǎng)絡(luò)狀態(tài)監(jiān)聽模塊 對應(yīng)AFNetworkReachabilityManager
Seriaalization:網(wǎng)絡(luò)通信信息序列化叽掘、反序列化模塊 對應(yīng) AFURLResponseSerialization
UIKit:對于iOS UIKit的擴(kuò)展庫
weak 內(nèi)部實(shí)現(xiàn)原理
weak是弱引用楣铁,所引用對象的計數(shù)器不會加一,并在引用對象被釋放的時候自動被設(shè)置為nil更扁。那么weak的原理是什么呢盖腕?
weak表其實(shí)是一個hash(哈希)表 (字典也是hash表),Key是所指對象的地址浓镜,Value是weak指針的地址集合鸿摇。weak通常用于解決循環(huán)引用問題暗挑。
下面就分析一下weak的工作原理冠句。
weak 實(shí)現(xiàn)原理的概括
Runtime維護(hù)了一個weak表军拟,用于存儲指向某個對象的所有weak指針。weak表其實(shí)是一個hash(哈希)表哄啄,Key是所指對象的地址雅任,Value是weak指針的地址(這個地址的值是所指對象指針的地址,就是地址的地址)集合(當(dāng)weak指針的數(shù)量小于等于4時,是數(shù)組, 超過時,會變成hash表)增淹。
weak 的實(shí)現(xiàn)原理可以概括以下三步:
1椿访、初始化時:runtime會調(diào)用objc_initWeak函數(shù)乌企,初始化一個新的weak指針指向?qū)ο蟮牡刂贰?br>
2虑润、添加引用時:objc_initWeak函數(shù)會調(diào)用 objc_storeWeak() 函數(shù), objc_storeWeak() 的作用是更新指針指向加酵,創(chuàng)建對應(yīng)的弱引用表拳喻。
3哭当、釋放時,調(diào)用clearDeallocating函數(shù)冗澈。clearDeallocating函數(shù)首先根據(jù)對象地址獲取所有weak指針地址的數(shù)組钦勘,然后遍歷這個數(shù)組把其中的數(shù)據(jù)設(shè)為nil,最后把這個entry從weak表中刪除亚亲,清理對象的記錄彻采。