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)系浮声?
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)行繪制
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?
-
若想令自己所寫的對象具有拷貝功能绎速,則需實(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是如何生成并添加到這個類中的坑夯?
-
@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)原理
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)行對稱加密
如上圖卦溢,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功能就會失效膳汪。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)就可以了。
當(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ù)才會返回瞪醋。
-
內(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哪個更精確
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示意圖:
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。
從上圖可以看出篮昧,從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.怎么檢測圖層混合
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ò)展庫