iOS基礎(chǔ)面試題
1、#import 跟#include 又什么區(qū)別,@class呢? #import<> 跟 #import"" 有什么區(qū)別?
- #import是OC引入頭類文件的關(guān)鍵字,#include是C/C++引入頭文件的關(guān)鍵字;#import引入的頭文件,無論你引入幾次,都會(huì)只做一次引入操作,防止重復(fù)倒入,相當(dāng)于#include+#pragma once;
- @class 是聲明類的,多用于頭文件相互引用;
- #import<>用于導(dǎo)入系統(tǒng)頭文件和三方庫,#import"" 用于導(dǎo)入用戶頭文件。
2、屬性atomic/nonatomic,assign/week/retain/strong/copy,readwrite/readonly ,@synthesize/@dynamic各是什么作用,在那種情況下用?
- atomic:原子性訪問屬性,會(huì)對(duì)setter和getter方法加鎖,保證屬性讀寫是安全的;但是在多線程中對(duì)集合類元素的添加刪除操作沒有加鎖,無法保證線程安全;由于對(duì)讀寫方法加鎖操作,導(dǎo)致性能降低。
- noatomic:非原子性訪問屬性,無法保證線程安全,但是性能比atomic有提升。由于atomic并不能完全保證線程安全,所以一般使用noatomic。
- assign:assign修飾基礎(chǔ)數(shù)據(jù)類型或?qū)ο箢愋停粫?huì)使引用計(jì)數(shù)+1;修飾對(duì)象類型時(shí),和week類似,但是在指向?qū)ο蟊会尫艜r(shí),不會(huì)把指針指向nil,容易造成野指針crash,所以一般用于修飾基礎(chǔ)數(shù)據(jù)類型。
- week:week只能修飾對(duì)象類型屬性,不會(huì)使引用計(jì)數(shù)+1,當(dāng)對(duì)象釋放時(shí),指針會(huì)指向nil;runtime會(huì)維護(hù)一個(gè)week表,是一個(gè)哈希表,key為week指針指向的對(duì)象的地址,value為所有指向這個(gè)對(duì)象的week指針的地址數(shù)組;當(dāng)該對(duì)象引用計(jì)數(shù)為0時(shí),runtime會(huì)根據(jù)對(duì)象地址查找對(duì)應(yīng)的弱引用數(shù)組,然后遍歷數(shù)組,把所有的week指針指向nil,避免野指針的出現(xiàn)。
- retain/strong:修飾對(duì)象屬性,使引用計(jì)數(shù)+1,ARC下一般使用strong;
- copy:一般用于NSString和block,屬性賦值時(shí),會(huì)copy一份新變量給屬性變量;
- readwrite:可讀寫屬性,默認(rèn)屬性修飾關(guān)鍵字;
- readonly:只讀屬性,不允許外部修改屬性變量;可通過KVC修改屬性變量值,但不建議。
- @synthesize:告訴編譯器,需要為該屬性生成setter和getter方法,默認(rèn)修飾;一般用于類實(shí)現(xiàn)protocol屬性時(shí);
- @dynamic:告訴編譯器,不生成setter和getter方法,將有程序員自己實(shí)現(xiàn)。
3、淺copy和深copy
- 淺copy是指針copy,copy一份指針,指向?qū)ο髢?nèi)存地址,使對(duì)象引用計(jì)數(shù)+1;
- 深copy是對(duì)象內(nèi)存copy,會(huì)copy一份對(duì)象到新的內(nèi)存地址,不會(huì)使對(duì)象引用計(jì)數(shù)+1;
- 不可變集合類的copy是淺copy,mutableCopy是深copy;
- 可變集合類的copy和mutableCopy都是深copy;
4、引用計(jì)數(shù)和AutoreleasePool
- 當(dāng)我們創(chuàng)建一個(gè)對(duì)象時(shí),它的引用計(jì)數(shù)會(huì)+1,系統(tǒng)會(huì)為對(duì)象分配內(nèi)存;每當(dāng)對(duì)象有一個(gè)新的引用,引用計(jì)數(shù)+1,每當(dāng)對(duì)象取消一個(gè)引用,引用計(jì)數(shù)-1;當(dāng)對(duì)象引用計(jì)數(shù)為0,系統(tǒng)會(huì)釋放該對(duì)象內(nèi)存。
- MRC下,使用retain方法使對(duì)象引用計(jì)數(shù)+1,避免被釋放;使用release使引用計(jì)數(shù)-1,釋放對(duì)對(duì)象的引用;
- ARC下,編譯器會(huì)自動(dòng)插入retain和release操作,不需要我們手動(dòng)管理內(nèi)存;
- AutoreleasePool是由AutoreleasePoolPage構(gòu)成的雙向鏈表,當(dāng)runloop啟動(dòng)時(shí),會(huì)創(chuàng)建AutoreleasePool,并將所有標(biāo)記延遲釋放的對(duì)象加入自動(dòng)釋放池,當(dāng)runloop退出時(shí),自動(dòng)釋放池里的對(duì)象會(huì)被釋放。(手動(dòng)創(chuàng)建的自動(dòng)釋放池里的對(duì)象會(huì)在AutoreleasePool{}大括號(hào)結(jié)尾處釋放);當(dāng)調(diào)用push方式時(shí),會(huì)把一個(gè)POOL_BOUNDARY對(duì)象加入page并返回其地址,當(dāng)調(diào)用pop方法時(shí)傳入POOL_BOUNDARY地址,并從最后入棧的對(duì)象開始一直到POOL_BOUNDARY的位置之間的對(duì)象,全部調(diào)用release,id *next地址指向下一個(gè)能存放auto release對(duì)象的地方;
- 由new、alloc、copy、mutableCopy關(guān)鍵字生成的對(duì)象會(huì)被生成者持有,ARC下系統(tǒng)會(huì)在合適的時(shí)機(jī)release,不會(huì)進(jìn)入AutoreleasePool,被一般類方法比如+(instancetype)createModelWith:創(chuàng)建的對(duì)象,會(huì)被標(biāo)記為autorelease,會(huì)被自動(dòng)加入最近創(chuàng)建的釋放池中;
- 子線程沒有啟動(dòng)runloop時(shí),沒有創(chuàng)建自動(dòng)釋放池,當(dāng)有對(duì)象被標(biāo)記為auto releasing時(shí),才會(huì)創(chuàng)建,并把對(duì)象加入自動(dòng)釋放池,當(dāng)子線程退出時(shí),自動(dòng)釋放池里的對(duì)象會(huì)被release。
常見的autoreleasepool使用
for(inti=0; i<1000000; i++) {
NSString * str = [NSString stringWithFormat:@"abcdefghijklmn1234566!@#$"];
}
for(inti=0; i<1000000; i++) {
@autoreleasepool {
NSString * str = [NSString stringWithFormat:@"abcdefghijklmn1234566!@#$"];
}
}
- 野指針和僵尸對(duì)象:指針指向的對(duì)象已被釋放,內(nèi)存被回收了,那么這個(gè)對(duì)象就叫僵尸對(duì)象,指向這個(gè)對(duì)象的指針,就是野指針。向指向nil的指針發(fā)送消息,不會(huì)crash,向指向僵尸對(duì)象的野指針發(fā)送消息會(huì)crash;
- Tagged Pointer:從64bit開始,iOS引入tagged pointer技術(shù),用于解決NSNumber、NSDate、NSString數(shù)據(jù)比較小時(shí)的存儲(chǔ)問題;tagged pointer之前,這些對(duì)象需要?jiǎng)討B(tài)分配內(nèi)存,對(duì)象的指針存儲(chǔ)的是堆上對(duì)象的地址,tagged pointer之后,NSNumber對(duì)象指針存儲(chǔ)的是tag+data,也就是指針直接存儲(chǔ)了數(shù)據(jù),當(dāng)指針不夠存儲(chǔ)數(shù)據(jù)時(shí),才會(huì)走動(dòng)態(tài)內(nèi)存分配,把對(duì)象存在堆上。
5、KVC、KVO
- KVC:健值編碼模式,是通過對(duì)象的成員變量名,對(duì)對(duì)象的成員變量進(jìn)行讀寫的方法。通過-setValue:forKey:寫值,通過-valueForKey:讀值;KVC在寫值和讀值時(shí)會(huì)優(yōu)先通過setter和getter方法,然后再去通過key值找對(duì)應(yīng)的變量;如果沒有找到,就會(huì)走-setValue:forUnderfindKey:和-valueForUnderfindKey:方法,如果這兩個(gè)方法沒有實(shí)現(xiàn),則程序?qū)rash。
- -setValue:forKey: 依次通過查找方法-setKey: 、-_setKey: 、-setIsKey: 如果找到就調(diào)用,如果沒找到,就走+(BOOL)accessInstanceVariablesDirectly方法,返回YES(默認(rèn)YES),接著找_key、_isKey、key、isKey成員變量,有的話直接賦值沒有的話走,-setValue:forUnderfindKey:;返回NO走-setValue:forUnderfindKey:
- -valueForKey: 依次通過-getKey、-key、-isKey、-_key方法,如果沒找到,就走+(BOOL)accessInstanceVariablesDirectly方法,返回YES(默認(rèn)YES),接著找_key、_isKey、key、isKey成員變量,有的話直接取值返回;
- -setValue:forKeyPath:和-valueForKeyPath用于對(duì)多級(jí)的對(duì)象屬性賦值,或者對(duì)集合類元素的屬性賦值
- 對(duì)于可變字典:NSMutableDictionary來說,使用-setValue:forKey:,內(nèi)部會(huì)走-setObject:forKey:,其中使用-setValue:forKey:時(shí),value可以為nil,為nil時(shí),會(huì)走-removeObjectForKey:刪除健值對(duì),key只能為字符串類型;使用-setObject:forKey:時(shí),value不能為nil,key可以為任意對(duì)象(遵守NSCopying協(xié)議的對(duì)象)
- KVO:健值監(jiān)聽,是觀察者模式的一種實(shí)現(xiàn)。KVO是基于runtime實(shí)現(xiàn)的,A對(duì)象對(duì)B對(duì)象的屬性的改變做出監(jiān)聽的一種開發(fā)模式;runtime會(huì)創(chuàng)建B類的一個(gè)名字為NSKVONotifying_B的子類,并把B對(duì)象的is a指針指向這個(gè)子類,子類重寫對(duì)應(yīng)的屬性setter方法,增加了-willChangeValueForKey:和-didChangeValueForKey:方法的調(diào)用,當(dāng)對(duì)應(yīng)屬性值改變時(shí)通知觀察者A對(duì)象,該屬性已經(jīng)改變。
6、代理、通知、KVO
三種模式都是對(duì)象之間事件傳遞的一種方式;
- 通知:一對(duì)多,多對(duì)多。優(yōu)點(diǎn)是一對(duì)多,多對(duì)多;可跨越多個(gè)層級(jí)之間傳遞消息,代碼解耦;缺點(diǎn)是字符串硬編碼,編譯期不容易排查錯(cuò)誤;代碼比較分散不容易維護(hù)。
- 代理:一對(duì)一;優(yōu)點(diǎn)是方案簡便,代碼比較緊湊,方便閱讀調(diào)試和維護(hù);缺點(diǎn)是代碼需要寫的比較多;典型的是TableView的delegate和dataSource;
- KVO:一對(duì)一;優(yōu)點(diǎn)實(shí)時(shí)監(jiān)聽對(duì)象變化,實(shí)現(xiàn)同步;高聚合,監(jiān)聽變化和處理變化在一起容易維護(hù);缺點(diǎn)是。。。感覺沒啥,主要看實(shí)際開發(fā)中適合選用那種方案。
7、擴(kuò)展(Extension)、分類(Category)、繼承、協(xié)議(Protocol);面向?qū)ο笠约癘C動(dòng)態(tài)語言特性
- 擴(kuò)展-Extension:擴(kuò)展是一種特殊的分類,可以在類聲明之外,為類聲明額外的屬性、實(shí)力變量、方法、協(xié)議等,但是不需要實(shí)現(xiàn)具體的@implementation;優(yōu)點(diǎn):一般用于封裝組件時(shí)隱藏不希望暴露的屬性和方法;缺點(diǎn)是所聲明屬性方法等只能在類內(nèi)部使用,不能被繼承;
- 分類-Category:在不改變現(xiàn)有類的情況下,為類添加新的方法;優(yōu)點(diǎn):不改變?cè)碱?;可以運(yùn)行時(shí)動(dòng)態(tài)調(diào)用新方法;可以拆分工作量大的類,使代碼結(jié)構(gòu)更清晰,更容易維護(hù);可以為系統(tǒng)類NSArray等常用類添加便捷方法,提高代碼復(fù)用。缺點(diǎn):不能調(diào)用原有類的私有屬性和方法;濫用分類覆蓋系統(tǒng)類方法,會(huì)造成不可控的風(fēng)險(xiǎn);多處對(duì)同一個(gè)類寫分類有可能會(huì)有命名沖突,導(dǎo)致方法覆蓋,出現(xiàn)不可控風(fēng)險(xiǎn)。
- 繼承:子類繼承父類的屬性和方法,提高代碼復(fù)用;繼承是面向?qū)ο蟮囊粋€(gè)基礎(chǔ);
- 協(xié)議-Protocol:協(xié)議是定義一套方法或?qū)傩?,但不提供?shí)現(xiàn),由具體的類實(shí)現(xiàn)協(xié)議的能力;優(yōu)點(diǎn)是一套協(xié)議由多個(gè)類實(shí)現(xiàn)相同接口不同的能力;
- 封裝(只暴露使用接口,隱藏內(nèi)部實(shí)現(xiàn)細(xì)節(jié))、繼承(子類繼承父類的屬性方法)、多態(tài)(運(yùn)行時(shí)才確定接口的具體實(shí)現(xiàn)),是OC為面向?qū)ο缶幊痰幕A(chǔ);
- 動(dòng)態(tài)是指:類型檢查和消息發(fā)送是在運(yùn)行時(shí)才確定的;動(dòng)態(tài)綁定、動(dòng)態(tài)類型、動(dòng)態(tài)加載是OC的動(dòng)態(tài)特性。
8、const、宏定義、static、extern
- const:常量修飾詞;修飾基礎(chǔ)數(shù)據(jù)類型時(shí),表示一個(gè)常量,值不可變,修飾指針類型變量時(shí),const關(guān)鍵字在左邊,指針的地址不可變,指針指向的值可變,const關(guān)鍵字在右邊,指針的地址可變,指針指向的值不可變;
- 宏定義:可以把常量,函數(shù)等聲明成宏,在編譯時(shí)替換;
- static:修飾局部變量,只分配一次內(nèi)存,局部變量生命周期會(huì)延長到程序生命周期;修飾全局變量,改變?nèi)肿兞孔饔糜颍o態(tài)全局變量只能在本文件內(nèi)訪問。
- extern:聲明外部變量;B類中定義的全局變量,可以在A類中用extern聲明之后使用,不需要再導(dǎo)入B頭文件。
9、OC、C、C++、Swift混編
.m文件只識(shí)別OC語法和C語法;
.mm文件識(shí)別OC、C、C++語法;
.cpp識(shí)別C++、C語法
- OC調(diào)用Swift,需要OC頭文件導(dǎo)入TargetName-Swift.h文件
- Swift調(diào)用OC,需要把OC頭文件加入TargetName-Bridging-Header.h頭文件里。
10、isa指針
實(shí)例對(duì)象的isa指針指向?qū)ο蟮念?,類里包含了?shí)例對(duì)象可使用的屬性成員變量方法等所有信息。類的isa指針指向元類,元類保存了類對(duì)象的方法和屬性等信息,元類的isa指針指向根元類,根元類的isa指針指向自己。
- 系統(tǒng)為NSObject對(duì)象分配16個(gè)字節(jié)的內(nèi)存,但是一般只占用8個(gè)字節(jié)。
// 打印對(duì)象的地址
NSLog(@"%p", obj);
// 打印指針的地址
NSLog(@"%p", &obj);
11、響應(yīng)者鏈和事件傳遞
- 事件傳遞:當(dāng)用戶觸摸屏幕時(shí),系統(tǒng)會(huì)把觸摸事件通過端口傳遞給前臺(tái)APP的UIApplication,由UIApplication傳遞給Window,window傳遞給最上層的視圖。事件的傳遞是由下到上,由-hitTest:withEvent:判斷觸摸點(diǎn)在不在當(dāng)前view,如果在的話就遍歷子view,依次找到最上層的view。
- 響應(yīng)者:繼承自UIResponder的類都可以成為響應(yīng)者。
- 響應(yīng)者鏈:由響應(yīng)者組成的事件響應(yīng)鏈條;當(dāng)一個(gè)事件傳遞給響應(yīng)者后,會(huì)判斷響應(yīng)者能不能處理事件,也就是有沒有實(shí)現(xiàn)-touchesBegan:withEvent:等系列方法或者是有手勢存在,如果沒有就確定這個(gè)view不處理這次觸摸事件,事件會(huì)傳遞給父view(如果是Controller的view,會(huì)出遞給Controller),依次向下傳遞,直到UIApplication,如果都不能處理事件,則事件會(huì)被廢棄。
12、block、__weak、__block,以及對(duì)變量的捕獲原理
- block:本質(zhì)是一個(gè)對(duì)象,有isa指針,內(nèi)部封裝了一個(gè)函數(shù)以及函數(shù)的調(diào)用環(huán)境,可以在需要的時(shí)候執(zhí)行。
- __weak :一般用于block解循環(huán)引用。
- __block:一般用于block內(nèi)部修改外部變量值時(shí),對(duì)外部變量的修飾詞。
- NSGlobalBlock:block內(nèi)部不捕獲外部局部變量,是全局block;
- NSStackBlock:block捕獲了外部局部變量,是棧block;
- NSMallocBlock:棧block進(jìn)行copy操作,會(huì)被copy到堆上(block的copy、block作為函數(shù)返回值、block被賦值給強(qiáng)指針、block作為OC里面帶有usingBlock的函數(shù)參數(shù)、block作為GCD函數(shù)參數(shù)時(shí),都會(huì)被copy到堆上)
為什么block要捕獲變量?
- 函數(shù)內(nèi)部定義的block和當(dāng)前函數(shù)的作用域不同,當(dāng)前函數(shù)內(nèi)定義的局部變量會(huì)在函數(shù)執(zhí)行結(jié)束時(shí)被釋放,但是block仍然有可能訪問變量,所以需要捕獲變量到block內(nèi)部。
為什么block能捕獲變量?
- 因?yàn)閎lock對(duì)象內(nèi)部生成了對(duì)應(yīng)的變量屬性,所謂捕獲,就是block內(nèi)部持有這個(gè)變量。
為什么block內(nèi)部不能直接修改局部變量值?
- 基本類型變量,是值copy,外部局部變量和block內(nèi)部的變量已經(jīng)不是同一個(gè)變量,所以不能修改;
- 對(duì)象類型變量,被捕獲的是變量的指針,是變量指針的copy;block內(nèi)部copy一份變量指針,指向?qū)ο蟮刂罚@個(gè)指針和外部指針不是同一個(gè)了,自然不能修改。
- 全局變量:block內(nèi)部引用全局變量,是直接訪問,不會(huì)發(fā)生捕獲。
- 局部變量-基本類型:捕獲基本類型是值copy,block保存了一份變量值到block內(nèi)部,
- 局部變量-對(duì)象類型:捕獲對(duì)象類型是指針copy,block內(nèi)部會(huì)生成一個(gè)對(duì)象類型的指針指向該對(duì)象,對(duì)象的引用計(jì)數(shù)會(huì)根據(jù)外部對(duì)象引用計(jì)數(shù)修飾詞(__strong、__weak)等決定是否+1;
- 靜態(tài)變量:靜態(tài)變量的生命周期會(huì)持續(xù)到App退出,所以對(duì)靜態(tài)變量的捕獲是指針的傳遞,block內(nèi)部直接訪問;
- __block修飾的變量:被__block修飾的局部變量,會(huì)被包裝成一個(gè)__block結(jié)構(gòu)體,它內(nèi)部持有指向局部變量的指針TestModel *model3,__Block_byref_model3_1 *__forwarding指向自己:
void *__isa;
__Block_byref_model3_1 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
TestModel *model3;
};
block在被copy到堆上時(shí),__block結(jié)構(gòu)體同時(shí)被copy到堆上,它的__forwarding指針指向被copy到堆上的結(jié)構(gòu)體內(nèi)存地址,block內(nèi)部通過__cself->model3.__forwarding->model3訪問原始變量的地址,從而修改值。
13、數(shù)據(jù)持久化
數(shù)據(jù)存儲(chǔ)的主要方式:
NSUserdefault:存儲(chǔ)輕量數(shù)據(jù)
NSKeyedArchiver:序列化存儲(chǔ),可以存儲(chǔ)實(shí)現(xiàn)NSCoding協(xié)議的model;
Sqlite:建立數(shù)據(jù)庫存儲(chǔ)數(shù)據(jù);
CoreData:Apple特有的一種數(shù)據(jù)庫支持方案,apple自動(dòng)建立了數(shù)據(jù)表和模型的關(guān)聯(lián),使用方式更加面向?qū)ο螅臃奖恪?br>
KeyChin:鑰匙串存儲(chǔ),一般存儲(chǔ)一些重要的、保密要求高的輕量數(shù)據(jù)。
14、id、NSObject、instancetype、nil、Nil、NULL、NSNULL的區(qū)別?
id:可以指向任意類型的對(duì)象,不會(huì)做編譯時(shí)檢查,不會(huì)做類型轉(zhuǎn)換。
NSObject:指向任意NSObject子類對(duì)象,會(huì)做編譯時(shí)檢查,會(huì)做類型轉(zhuǎn)換。
instancetype:只能作為方法的返回值,會(huì)做類型校驗(yàn)。
nil:空實(shí)例對(duì)象。
Nil:空類對(duì)象.
NULL:空基本數(shù)據(jù)類型。
NSNULL:在不能使用nil的時(shí)候,表示一個(gè)空對(duì)象。
15、BOOL和bool
在64位操作系統(tǒng)或者ArmV7k后,BOOL其實(shí)就是bool
在32位系統(tǒng)中,BOOL是signer char,取值-128~127,當(dāng)BOOL a = 8960時(shí),超過一個(gè)字節(jié),會(huì)把8960轉(zhuǎn)化為二進(jìn)制0010 0011 0000 0000,取后八位0000 0000,也就是a = 0,會(huì)判斷出a為NO。
另外下面的比較也會(huì)出問題,慎用 == YES:
if (c == YES) {
NSLog(@"c YES");
} else {
NSLog(@"c NO");
}
16、NSSet 、NSArray、NSPointerArray、NSHashTable、NSDictionary、NSMapTable、NSCache
NSSet:無序集合,只能存放對(duì)象,會(huì)自動(dòng)去重,查詢?cè)厮俣群芸欤?br>
NSArray:有序集合,只能存放對(duì)象,在內(nèi)存中是連續(xù)的,增加刪除比較快;
NSPointerArray:有序集合,允許NULL元素加入集合,可以使用weak修飾集合元素,而NSSet和NSArray對(duì)集合內(nèi)對(duì)象的必須是強(qiáng)引用的;
NSHashTable:無序集合,可以使用weak修飾集合元素
NSDictionary:健值對(duì),key必須遵循NSCopyding,value為非空對(duì)象;
NSMapTable:類似NSDictionary,可以對(duì)key或value設(shè)置是否強(qiáng)引用;
NSCache:健值存儲(chǔ),不會(huì)對(duì)key強(qiáng)引用,當(dāng)發(fā)生內(nèi)存警告,NSCache會(huì)自動(dòng)清除緩存,NSCache線程安全,非常合適做緩存。
17、runtime
動(dòng)態(tài)運(yùn)行時(shí),是OC語言的特性之一。runtime使OC的方法調(diào)用,在編譯時(shí)不會(huì)去確定具體調(diào)用的方法,而是在運(yùn)行時(shí)動(dòng)態(tài)查找執(zhí)行方法。
OC里面對(duì)象調(diào)用方法,其實(shí)是向這個(gè)對(duì)象發(fā)送消息,通過對(duì)象的isa指針找到對(duì)象的類,從類的方法列表里面查找方法,從而執(zhí)行。
runtime常見用法:
1、交換方法實(shí)現(xiàn),使已有類的方法有新的能力。
2、動(dòng)態(tài)添加方法
3、添加關(guān)聯(lián)屬性
4、實(shí)現(xiàn)模型字典轉(zhuǎn)換
5、實(shí)現(xiàn)自動(dòng)歸解檔
7、消息轉(zhuǎn)發(fā),可以規(guī)避方法未找到的crash
18、進(jìn)程
在iOS中,一個(gè)APP啟動(dòng),就是開啟一個(gè)進(jìn)程,一個(gè)進(jìn)程有單獨(dú)的內(nèi)存空間、系統(tǒng)資源、端口等,進(jìn)程可以開啟多個(gè)線程執(zhí)行任務(wù),各線程共享進(jìn)程的資源。進(jìn)程間通信可以通過Port(端口)、URL Scheme、Keychain、UIPasteboard(剪切板)、UIDocumentInterationController(共享文檔)、AirDrop、UIActivityController(分享面板)、App Groups(系統(tǒng)分享)
19、多線程
多線程,是同時(shí)開辟多個(gè)線程,并發(fā)執(zhí)行任務(wù),提升性能的技術(shù)。同一時(shí)間一個(gè)CPU核心只能處理一個(gè)線程,所謂的并發(fā)執(zhí)行,就是CPU快速在多個(gè)線程之間調(diào)度,所呈現(xiàn)的同時(shí)執(zhí)行的假象,它的優(yōu)勢是充分利用多核處理器的優(yōu)勢,使多個(gè)任務(wù)可以在多核CPU中同時(shí)執(zhí)行,他的缺點(diǎn)是新線程會(huì)占用空間,線程間切換會(huì)有時(shí)間開銷。
常見的多線程方案:
p_thread、NSThread、GCD、NSOperation。
常見的多線程通信方案:
1、訪問同一個(gè)變量
2、通過- performSelect:
3、線程間加鎖
4、通過NSPort傳遞信息
p_thread:是一套C的接口,需要手動(dòng)管理線程生命周期,使用難度大;
NSThread:OC接口,使用簡單,多用于簡單的開啟線程執(zhí)行任務(wù),不適合復(fù)雜的線程操作,需要手動(dòng)管理線程生命周期,常見使用方式:
-performSelector: onThread: withObject: waitUntilDone:
-performSelectorOnMainThread: withObject: waitUntilDone:
GCD:一套C接口,能充分利用多核技術(shù),不需要管理線程生命周期,使用便捷
dispatch_sync:同步執(zhí)行任務(wù)
dispatch_async:異步執(zhí)行任務(wù)
dispatch_group_t:調(diào)度組,配合dispatch_group_async、dispatch_group_notify實(shí)現(xiàn)執(zhí)行完一組操作之后,再執(zhí)行接下來的操作。
dispatch_barrier_(a)sync:柵欄函數(shù),會(huì)等待barrier任務(wù)之前的任務(wù)執(zhí)行完之后,再執(zhí)行barrier提交的任務(wù)
dispatch_semaphore:信號(hào)量,一種鎖,用于等待異步任務(wù)執(zhí)行結(jié)束后再執(zhí)行后續(xù)任務(wù)
dispatch_once_token:多用于創(chuàng)建單例
GCD取消任務(wù):只能取消未開始執(zhí)行的任務(wù)
通過dispatch_block_cancle取消加入GCD且未開始執(zhí)行的block
通過bool變量+return操作,使未執(zhí)行的block不再執(zhí)行。
NSOperation:apple對(duì)GCD封裝的一套面向?qū)ο蟮亩嗑€程接口,不需要管理線程生命周期,可以實(shí)現(xiàn)復(fù)雜的線程操作(添加線程間依賴等)
NSOptration是抽象類,只提供了接口,沒有類的實(shí)現(xiàn)。使用NSOptration主要使用NSBlockOperation、NSInvocationOperation兩個(gè)子類,當(dāng)然也可以自定義NSOptration實(shí)現(xiàn)多線程操作,通過兩個(gè)子類創(chuàng)建任務(wù),添加到NSOperationQueue里面執(zhí)行。
-addDependency:添加線程間依賴
-cancle:取消未執(zhí)行的任務(wù)
-cancelAllOperations:取消所有未執(zhí)行的任務(wù)
線程安全:在多線程執(zhí)行任務(wù)時(shí),有可能多個(gè)線程同時(shí)訪問同一個(gè)資源,并對(duì)資源做出修改,容易引起資源數(shù)據(jù)混亂,造成程序運(yùn)行不可預(yù)測,所以需要保證線程安全。
加鎖:保證線程在訪問資源時(shí),不會(huì)有其他線程同時(shí)操作這個(gè)資源
設(shè)置線程同步執(zhí)行:同步執(zhí)行任務(wù),保證了對(duì)資源有序訪問。
自旋鎖:自旋鎖會(huì)忙等,也就是當(dāng)一個(gè)線程訪問一塊加鎖資源,線程不會(huì)休眠,會(huì)執(zhí)行while循環(huán),直到資源解鎖
互斥鎖:線程會(huì)休眠,也就是當(dāng)一個(gè)線程訪問一塊加鎖資源,會(huì)休眠,CPU可以調(diào)度其他線程執(zhí)行其他操作,當(dāng)資源解鎖時(shí),通知該線程喚醒并開始執(zhí)行
死鎖:多個(gè)操作互相等待對(duì)方執(zhí)行完成,才執(zhí)行自己,導(dǎo)致任務(wù)卡住,造成死鎖。
常見鎖:
OSSpinLock:自旋鎖,iOS10.0后已被廢棄
os_unfair_lock:iOS10.0之后,用于取代OSSpinLock,并不會(huì)使線程忙等
pthread_mutex_t:互斥鎖
NSLock:apple對(duì)mutex的封裝
NSConditionLock:遞歸鎖(多次加鎖不會(huì)造成死鎖),apple對(duì)mutex的封裝
@ synchronized:apple對(duì)mutex的封裝
dispatch_semaphore:最常使用的鎖,性能也比較好
pthread_rwlock_t:讀寫鎖,當(dāng)多線程訪問同一塊資源時(shí),對(duì)讀操作不加鎖,對(duì)寫操作加鎖;也就是多個(gè)讀操作的線程會(huì)同時(shí)執(zhí)行,當(dāng)有線程進(jìn)行寫操作執(zhí)行時(shí)才進(jìn)行加鎖,其他線程讀操作和寫操作等待寫操作執(zhí)行完成,多用于IO操作文件讀寫時(shí),提高了效率。
20、runloop
runloop:是一個(gè)事件處理循環(huán)機(jī)制,負(fù)責(zé)事件接收和分發(fā),是多線程的框架的基礎(chǔ)構(gòu)成。一個(gè)runloop對(duì)應(yīng)一個(gè)線程,對(duì)應(yīng)關(guān)系在一個(gè)全局字典里,key為線程,value為runloop,開啟線程時(shí),并不會(huì)開啟runloop,當(dāng)?shù)谝淮潍@取runloop時(shí),runloop才被創(chuàng)建。
runloop的運(yùn)行模式:
NSDefaultRunLoopMode:默認(rèn)運(yùn)行模式,主線程在這種模式下運(yùn)行
UITrackingRunLoopMode:頁面滑動(dòng)時(shí)的模式
NSRunLoopCommonModes:NSDefaultRunLoopMode+ UITrackingRunLoopMode
runloop運(yùn)行邏輯:
source0:非基于端口的事件源,通常是用戶觸摸事件、select事件等,是app內(nèi)部生成的事件源,不基于端口傳遞。
source1:基于端口事件源,一般是系統(tǒng)間的通信、網(wǎng)絡(luò)請(qǐng)求等,基于端口傳遞消息。
1、通知observer即將進(jìn)入runloop
2、同時(shí)observe即將處理timer
3、通知observer即將處理source0
4、處理source0
5、如有source1,且已經(jīng)準(zhǔn)備好,則進(jìn)入第9步
6、通知observe,即將休眠
7、休眠中,等待喚醒(timer源時(shí)間到了,手動(dòng)喚醒,source0事件源)
8、通知observe,即將被喚醒
9、處理喚醒時(shí)收到的消息(timer,source1),然后跳轉(zhuǎn)到第2步
10、通知observe,即將退出runloop
常駐線程:
@interface WYYThread : NSThread
@end
@implementation WYYThread
- (void)dealloc {
NSLog(@"WYYThread - %s", __func__);
}
@end
@interface TestThread ()
@property (nonatomic, strong) WYYThread *thread;
@property (nonatomic, assign) BOOL thread_stop;
@end
@implementation TestThread
- (instancetype)init {
if (self = [super init]) {
self.thread_stop = NO;
__weak typeof(self) weakSelf = self;
self.thread = [[WYYThread alloc] initWithBlock:^{
[[NSRunLoop currentRunLoop] addPort:[NSPort new] forMode:NSDefaultRunLoopMode];
while (weakSelf != nil && !weakSelf.thread_stop) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
NSLog(@"runloop 結(jié)束");
}];
self.thread.name = @"TestThread";
[self.thread start];
}
return self;
}
- (void)addTask:(dispatch_block_t)block {
[self performSelector:@selector(takeTask:) onThread:self.thread withObject:block waitUntilDone:YES];
}
- (void)takeTask:(dispatch_block_t)block {
block();
}
- (void)takeStopTask {
self.thread_stop = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
}
- (void)stop {
[self performSelector:@selector(takeStopTask) onThread:self.thread withObject:nil waitUntilDone:YES];
}
- (void)dealloc {
NSLog(@"TestThread - %s", __func__);
[self stop];
}
@end
21、網(wǎng)絡(luò)相關(guān)
HTTP協(xié)議:
超文本傳輸協(xié)議,使用80端口,是基于TCP/IP的應(yīng)用層協(xié)議,規(guī)定了客戶端和服務(wù)器之前傳輸數(shù)據(jù)的規(guī)則,HTTP不只能傳輸文本,可以傳輸任意類型數(shù)據(jù)。
常用方法
GET:用于向服務(wù)器請(qǐng)求數(shù)據(jù),參數(shù)跟在URL后面,有參數(shù)長度限制,明文傳遞數(shù)據(jù)
POST:用于向服務(wù)端提交數(shù)據(jù),數(shù)據(jù)放在包體里,理論上沒有參數(shù)大小限制,相比于GET會(huì)更安全。
HEAD:類似GET,單數(shù)請(qǐng)求返回?cái)?shù)據(jù)里沒有包體,只有請(qǐng)求頭
DELETE:向服務(wù)器請(qǐng)求刪除數(shù)據(jù)
PUT:向服務(wù)器請(qǐng)求更新數(shù)據(jù)
特點(diǎn):
靈活:可以傳輸任意類型數(shù)據(jù)
無連接:一次鏈接只發(fā)送一次請(qǐng)求
無狀態(tài):不記錄請(qǐng)求狀態(tài),斷聯(lián)需要重傳
聯(lián)接過程:
客戶端向服務(wù)端發(fā)起連接
服務(wù)端客戶端建立連接,客戶端向服務(wù)端請(qǐng)求數(shù)據(jù)
服務(wù)端響應(yīng)客戶端請(qǐng)求
客戶端收到請(qǐng)求,關(guān)閉連接
請(qǐng)求報(bào)文:
請(qǐng)求行:包含請(qǐng)求方式(Method)、請(qǐng)求URL、HTTP版本號(hào)
請(qǐng)求頭:請(qǐng)求附加數(shù)據(jù),accept:客戶端接收的資源類型(如text/html),content-length:數(shù)據(jù)長度等
請(qǐng)求體:請(qǐng)求數(shù)據(jù)
響應(yīng)報(bào)文:
響應(yīng)行:服務(wù)器返回的狀態(tài)內(nèi)容,HTTP版本號(hào)、狀態(tài)碼等
響應(yīng)頭:和請(qǐng)求頭類似
響應(yīng)體:具體的響應(yīng)數(shù)據(jù)
HTTPS:超文本傳輸安全協(xié)議,使用443端口,是由HTTP+SSL組成,旨在解決HTTP傳輸不安全的問題。
HTTPS連接過程:
客戶端向服務(wù)器請(qǐng)求連接
服務(wù)器申請(qǐng)CA證書,發(fā)送證書包含公鑰給客戶端
客戶端通過根證書確認(rèn)服務(wù)器證書有效性,進(jìn)行身份確認(rèn)
客戶端生成對(duì)稱加密密鑰,通過公鑰加密,發(fā)送給服務(wù)器
服務(wù)器收到加密密鑰,使用私鑰解密對(duì)稱加密的密鑰
客戶端和服務(wù)器使用對(duì)稱加密密鑰進(jìn)行數(shù)據(jù)傳輸
對(duì)稱加密:加密解密使用同一密鑰,加解密速度很快,使用很廣泛,常見有AES算法,DES算法,3DES算法等
非對(duì)稱加密:生成公鑰私鑰一對(duì)密鑰,加解密使用不同的密鑰,私鑰需要嚴(yán)格保存不能泄漏。常見RAS算法。
TCP:傳輸控制協(xié)議
面向連接可靠的基于字節(jié)流的傳輸層控制協(xié)議,是為了在不可靠的網(wǎng)絡(luò)中傳輸局?jǐn)?shù)據(jù)定義的一套協(xié)議,有三次握手建立連接,有確認(rèn)、窗口、重傳、擁塞控制機(jī)制,傳輸完成后,會(huì)斷開連接(四次揮手)節(jié)省資源
第一次握手:客戶端->服務(wù)器 發(fā)送SYN標(biāo)志位 序列號(hào)為X(隨機(jī)生成的,標(biāo)志數(shù)據(jù)傳輸序列的數(shù)字)的數(shù)據(jù)包,請(qǐng)求建立連接
第二次握手:服務(wù)端收到客戶端SYN數(shù)據(jù)包,想客戶端發(fā)送SYN+ACK數(shù)據(jù)包,SYN包中有服務(wù)器生成的隨機(jī)序列號(hào)Y,ACK包序列號(hào)為X+1,表示服務(wù)端收到客戶端請(qǐng)求,同意建立連接。
第三次握手:客戶端收到服務(wù)器SYN+ACK包,向服務(wù)器發(fā)送ACK包,序列號(hào)為Y+1,聯(lián)接建立,開始數(shù)據(jù)傳輸
數(shù)據(jù)傳輸:TCP數(shù)據(jù)會(huì)被分成TCP段,包含固定長度的頭部,和數(shù)據(jù)部分,頭部數(shù)據(jù)包含源端口號(hào),目標(biāo)端口號(hào),序列號(hào),確認(rèn)應(yīng)答號(hào),標(biāo)志位等信息。
序列號(hào):發(fā)送方在發(fā)送數(shù)據(jù)時(shí),對(duì)每個(gè)字節(jié)數(shù)據(jù)進(jìn)行編碼,序列號(hào)就是該段數(shù)據(jù)第一個(gè)字節(jié)的編碼,接收方通過該序列號(hào)對(duì)接收到的TCP段進(jìn)行重組,確保數(shù)據(jù)完整性和順序性。
確認(rèn)應(yīng)答號(hào):接收方在收到TCP段后,會(huì)向發(fā)送方發(fā)送ACK包,確認(rèn)應(yīng)答號(hào)為接收方期望接收的下一個(gè)序列號(hào)的TCP段,發(fā)送方根據(jù)收到的確認(rèn)應(yīng)答號(hào)確認(rèn)接收方有沒有正確接收數(shù)據(jù),如果沒有收到,或確認(rèn)應(yīng)答號(hào)不對(duì),則觸發(fā)重傳。
滑動(dòng)窗口機(jī)制:在接收方和發(fā)送方之間維護(hù)緩沖區(qū),暫存發(fā)送方已發(fā)送,接收方未應(yīng)答的TCP段,發(fā)送方可以連續(xù)發(fā)送多個(gè)TCP段,隨著接收方不斷接收確認(rèn)數(shù)據(jù),窗口向前滑動(dòng),發(fā)送發(fā)可以連續(xù)發(fā)送新的數(shù)據(jù)。
第一次揮手:客戶端完成數(shù)據(jù)傳輸之后,向服務(wù)端發(fā)送FIN數(shù)據(jù)包,請(qǐng)求關(guān)閉連接
第二次揮手:服務(wù)器收到客戶端FIN數(shù)據(jù)包,向客戶端發(fā)送ACK數(shù)據(jù)包,表示已知客戶端數(shù)據(jù)發(fā)送完畢,但此時(shí)服務(wù)器數(shù)據(jù)有可能沒有發(fā)送完畢,則繼續(xù)發(fā)送。
第三次揮手:服務(wù)端數(shù)據(jù)發(fā)送完畢,向客戶端發(fā)送FIN數(shù)據(jù)包,請(qǐng)求關(guān)閉連接
第四次揮手:客戶端向服務(wù)器發(fā)送ACK包,連接關(guān)閉。
UDP:數(shù)據(jù)報(bào)協(xié)議
對(duì)應(yīng)TCP,UDP是非連接的協(xié)議,不與對(duì)方建立連接,直接把數(shù)據(jù)發(fā)送過去,適合對(duì)及時(shí)性要求高,數(shù)據(jù)量小,且對(duì)質(zhì)量要求不高的場景。
Socket:基于對(duì)TCP/IP的封裝,提供一套接口,用于兩個(gè)程序通過雙向連接傳輸數(shù)據(jù)。也就是常說的長連接。
心跳:長連接中,用于保證長連接有效的機(jī)制,客戶端向服務(wù)器發(fā)送心跳包,收到服務(wù)器響應(yīng)確認(rèn)長連接有效。
DNS:域名解析服務(wù),用于域名和IP地址的映射,是互聯(lián)網(wǎng)提供的一種服務(wù)。
DNS劫持:域名劫持,攔截域名解析請(qǐng)求,返回假的域名地址或使域名無法訪問。
網(wǎng)絡(luò)七層模型:
應(yīng)用層:網(wǎng)絡(luò)服務(wù)與用戶的接口,有HTTP協(xié)議
表示層:數(shù)據(jù)的表示,安全,壓縮等
會(huì)話層:本地主機(jī)與遠(yuǎn)程主機(jī)連接、管理、終止
傳輸層:定義數(shù)據(jù)傳輸?shù)膮f(xié)議、端口等,以及流控和差錯(cuò)校驗(yàn)。TCP、UDP等協(xié)議
網(wǎng)絡(luò)層:進(jìn)行邏輯地址尋址,實(shí)現(xiàn)不同網(wǎng)絡(luò)之間的路徑選擇
數(shù)據(jù)鏈路層:建立邏輯連接、進(jìn)行硬件地址尋址、差錯(cuò)校驗(yàn) [2] 等功能
物理層:建立、維護(hù)、斷開物理連接
斷點(diǎn)續(xù)傳:
使用HTTP:利用HTTP頭部有Range字段,請(qǐng)求的數(shù)據(jù)的具體部分,然后拼接在已請(qǐng)求到的數(shù)據(jù)之后。
22、性能優(yōu)化
畫面卡頓:iOS屏幕刷新頻率是一秒鐘60幀,也就是16.7ms刷新一次屏幕,而屏幕上一幀畫面是由CPU和GPU協(xié)作完成的,CPU負(fù)責(zé)對(duì)象的創(chuàng)建、布局計(jì)算等數(shù)據(jù)處理,GPU負(fù)責(zé)把CPU準(zhǔn)備好的數(shù)據(jù)進(jìn)行渲染,渲染完成之后,會(huì)把畫面放到幀緩沖區(qū)。那么當(dāng)CPU處理數(shù)據(jù)時(shí)間占用過多,或者GPU渲染耗時(shí)過多,沒有在16.7ms之內(nèi)把渲染好的數(shù)據(jù)放到幀緩沖區(qū),就會(huì)出現(xiàn)掉幀,就會(huì)造成畫面卡頓。
離屏渲染:iOS是雙緩存機(jī)制,有前幀緩存和后幀緩存,GPU會(huì)把需要顯示的屏幕數(shù)據(jù)渲染好放進(jìn)前幀緩存,提供給視頻讀取器讀取,會(huì)把這一幀之后需要顯示的數(shù)據(jù)渲染在后幀緩存,當(dāng)接收到Vsync信號(hào)后,會(huì)把視頻讀取器指向前幀緩存的指針指向后幀緩存,前幀緩存的數(shù)據(jù)被丟棄,變?yōu)楹髱彺妗PU在渲染時(shí)遵循“畫家算法”,由遠(yuǎn)及近渲染畫面。當(dāng)我們?cè)O(shè)置光柵化(shouldRasterize)、添加遮罩(mask)、圓角(coreRadius)、陰影(shadow)等時(shí),由于前幀緩存使用過之后數(shù)據(jù)已經(jīng)沒了,我們又要對(duì)這些數(shù)據(jù)進(jìn)行設(shè)置,所以只好再開辟一塊緩存區(qū),用于操作已經(jīng)渲染的數(shù)據(jù),當(dāng)這些操作做完后,再把渲染好的數(shù)據(jù)放入離屏幀緩沖區(qū)用于顯示。這種開辟新的非屏幕當(dāng)前緩沖區(qū)來渲染數(shù)據(jù)的方式,就是離屏渲染。
離屏渲染觸發(fā)卡頓:1、創(chuàng)建新的幀緩沖區(qū)有資源消耗;2、從當(dāng)前屏幕緩沖區(qū)切換到離屏緩沖區(qū),顯示完之后需要再切換回當(dāng)前屏幕幀緩沖區(qū),多次上下文切換浪費(fèi)時(shí)間。
性能優(yōu)化:
CPU-優(yōu)化:
1、使用Table View的cell重用機(jī)制
2、Table View高度做緩存,因?yàn)楫?dāng)滑動(dòng)時(shí),highForRow會(huì)頻繁調(diào)用,緩存高度避免重復(fù)計(jì)算
3、減少視圖層級(jí),減少容易引起離屏渲染的操作
4、Table View刷新不要盲目的調(diào)用reloadData,有時(shí)候只刷新一個(gè)cell就行
5、當(dāng)view不需要響應(yīng)事件時(shí),盡量用Layer顯示圖像
6、在布局變動(dòng)時(shí),盡量提前計(jì)算好所有布局,統(tǒng)一提交改動(dòng),不要一個(gè)屬性一個(gè)屬性的改動(dòng)
7、AutoLayout最終會(huì)轉(zhuǎn)化為frame,所以會(huì)比frame更慢
8、盡量把耗時(shí)操作放在子線程
GPU-優(yōu)化:
1、避免短時(shí)間內(nèi)顯示大量圖片,合成一張顯示
2、大圖片要分片展示
3、減少視圖數(shù)量和層級(jí)
4、減少透明圖層
5、減少離屏渲染
離屏渲染-優(yōu)化:
1、使用UIBezierPath畫圓角,用shadowPath指定陰影效果路徑,避免裁剪等方式
2、設(shè)置layer的opaque為YES,避免復(fù)雜圖層的合成
3、盡量不使用包含透明通道(alpha)的圖片
4、最好讓美工做出相應(yīng)效果(陰影、圓角)的圖片
APP 啟動(dòng)過程:
dyld:加載Mach-O文件,連接動(dòng)態(tài)庫
runtime:當(dāng)dyld完成所有可執(zhí)行文件加載,動(dòng)態(tài)庫連接之后,會(huì)通知runtime進(jìn)行下一步
調(diào)用map_images進(jìn)行可執(zhí)行文件的解析和處理
在load_images中調(diào)用call_load_methods,執(zhí)行所有類和分類的+load方法
進(jìn)行各種Objc結(jié)構(gòu)的初始化(注冊(cè)O(shè)bjc類,初始化類對(duì)象)
調(diào)用C++靜態(tài)初始化器和attribute((constructor))修飾的函數(shù)
至此,可執(zhí)行文件周所有的符號(hào)(class、protocol、IMP、selector等)都已經(jīng)加載完畢
main:初始化工作完成,開始調(diào)用main函數(shù),UIApplicationMain函數(shù),APPDelegate的application:didFinishLaunchingWithOptions:函數(shù),至此APP啟動(dòng)完成
APP啟動(dòng)優(yōu)化:
減少動(dòng)態(tài)庫
減少類,分類,方法等
swift盡量用struct
+load 方法入無必要,放在+initialize中
application:didFinishLaunchingWithOptions:只做必要的工作,其他的工作可以等啟動(dòng)完成后再做
23、常見三方庫
SDWebImage
三個(gè)核心類:SDWebImageManager、SDImageCache、SDImageDownloader
當(dāng)加載圖片時(shí),會(huì)由SDWebImageManager執(zhí)行l(wèi)oadImageWithUrl:
首先由SDImageCache在內(nèi)存緩存中找,找到則回調(diào)顯示圖片;
未找到則去磁盤緩存中找,找到的話,就把圖片緩存到內(nèi)存中,再回調(diào)顯示圖片;
未找到則由SDImageDownloader開始下載圖片,下載完成后,則緩存到內(nèi)存和磁盤中,回調(diào)顯示圖片。
緩存是以圖片URL的MD5值為key,圖片為Value
SDImageDownloader的最大并發(fā)數(shù)為6
超時(shí)時(shí)間為15s
最大緩存時(shí)長為7天
AFNetworking
2.0是對(duì)NSURLConnection的封裝,沒用過
3.0是對(duì)NSURLSession的封裝
核心架構(gòu):
URLSession:進(jìn)行網(wǎng)絡(luò)通信
Serialization:進(jìn)行序列化操作
Reachability:網(wǎng)絡(luò)狀態(tài)監(jiān)聽
Security:證書等安全認(rèn)證
UIKit:對(duì)UI控件的擴(kuò)展