iOS面試題整理

UIKit

1.UIView 和 CALayer 是什么關(guān)系剑刑? \color{red}{★★}

  • 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ū)別? \color{red}{★★}

  • Bounds:一般是相對于自身來說的众辨,是控件的內(nèi)部尺寸。如果你修改了 Bounds舷礼,那么子控件的相對位置也會發(fā)生改變泻轰。

  • Frame :是相對于父控件來說的,是控件的外部尺寸且轨。

3.setNeedsDisplay 和 layoutIfNeeded 兩者是什么關(guān)系浮声?\color{red}{★★★}

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的理解\color{red}{★★★}

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的作用?\color{red}{★}

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有什么影響吭从?\color{red}{★★}

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ū)別\color{red}{★★}

  • 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ū)別驱还?\color{red}{★★}

  • nil、NIL 可以說是等價的凸克,都代表內(nèi)存中一塊空地址议蟆。

  • NSNULL 代表一個指向 nil 的對象。

2.如何實(shí)現(xiàn)一個線程安全的 NSMutableArray?\color{red}{★★★}

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 修飾的屬性是絕對安全的嗎奄妨?為什么?\color{red}{★★}

不是苹祟,所謂的安全只是局限于 Setter砸抛、Getter 的訪問器方法而言的,你對它做 Release 的操作是不會受影響的树枫。這個時候就容易崩潰了直焙。

4.實(shí)現(xiàn) isEqual 和 hash 方法時要注意什么?\color{red}{★★}

  • 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ū)別砂轻?\color{red}{★★★}

  • 相同點(diǎn)

    instancetype 和 id 都是萬能指針奔誓,指向?qū)ο蟆?/p>

  • 不同點(diǎn):

    1.id 在編譯的時候不能判斷對象的真實(shí)類型,instancetype 在編譯的時候可以判斷對象的真實(shí)類型搔涝。

    2.id 可以用來定義變量厨喂,可以作為返回值類型,可以作為形參類型庄呈;instancetype 只能作為返回值類型杯聚。

6.self和super的區(qū)別\color{red}{★★}

  • 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分別有什么作用甘穿?\color{red}{★★★}

  • @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ū)別?\color{red}{★★}

  • __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.類族\color{red}{★}

系統(tǒng)框架中有許多類簇君躺,大部分collection類都是類族。例如NSArray與其可變版本NSMutableArray开缎。這樣看來實(shí)際上有兩個抽象基類棕叫,一個用于不可變數(shù)組,一個用于可變數(shù)組奕删。盡管具備公共接口的類有兩個谍珊,但任然可以合起來算一個類族。不可變的類定義了對所有數(shù)組都通用的方法,而可變類則定義了那些只適用于可變數(shù)組的方法砌滞。兩個類共同屬于同一個類族侮邀,這意味著二者在實(shí)現(xiàn)各自類型的數(shù)組時可以共用實(shí)現(xiàn)代碼,此外還能把可變數(shù)組復(fù)制成不可變數(shù)組,反之亦然。

10.struct和class的區(qū)別\color{red}{★}

  • 類: 引用類型(位于棧上面的指針(引用)和位于堆上的實(shí)體對象)

  • 結(jié)構(gòu):值類型(實(shí)例直接位于棧中)

WebView

1.說一下 JS 和 OC 互相調(diào)用的幾種方式博投?\color{red}{★★★}

  • 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 時遇到過哪些問題置尔?\color{red}{★}

白屏問題,Cookie 問題缠借,在WKWebView上直接使用NSURLProtocol無法攔截請求,在WKWebView 上通過loadRequ發(fā)起的post請求body數(shù)據(jù)被丟失,截屏問題等

內(nèi)存管理

1.什么情況使用weak關(guān)鍵字亡笑,相比assign有什么不同?\color{red}{★★★★}

  • 什么情況使用 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?\color{red}{★★★★}

  • 若想令自己所寫的對象具有拷貝功能绎速,則需實(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.深拷貝與淺拷貝\color{red}{★★★★★}

淺拷貝只是對指針的拷貝知残,拷貝后兩個指針指向同一個內(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是如何生成并添加到這個類中的坑夯?\color{red}{★★★★}

  • @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\color{red}{★★★★★}

  • 在 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)畴栖?俭厚?\color{red}{★★★★★}

簡單說是雙向鏈表,每張鏈表頭尾相接驶臊,有 parent挪挤、child指針

每創(chuàng)建一個池子,會在首部創(chuàng)建一個 哨兵 對象,作為標(biāo)記

最外層池子的頂端會有一個next指針关翎。當(dāng)鏈表容量滿了扛门,就會在鏈表的頂端,并指向下一張表纵寝。

7.BAD_ACCESS在什么情況下出現(xiàn)论寨?\color{red}{★★★}

訪問了懸垂指針,比如對一個已經(jīng)釋放的對象執(zhí)行了release爽茴、訪問已經(jīng)釋放對象的成員變量或者發(fā)消息葬凳。 死循環(huán)

8.使用CADisplayLink、NSTimer有什么注意點(diǎn)室奏?\color{red}{★★★★}

CADisplayLink火焰、NSTimer會造成循環(huán)引用,可以使用YYWeakProxy或者為CADisplayLink胧沫、NSTimer添加block方法解決循環(huán)引用

9.iOS內(nèi)存分區(qū)情況\color{red}{★★★★}

  • 棧區(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)存管理方式\color{red}{★★★★★}

  • 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)引用\color{red}{★★★★★}

循環(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)原理\color{red}{★★★★}

  • 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)原理\color{red}{★★★★★}

KVO

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ū)別\color{red}{★★★★}

  • 通知是觀察者模式俩垃,適合一對多的場景

  • 代理模式適合一對一的反向傳值

  • 通知耦合度低励幼,代理的耦合度高

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)鍵字\color{red}{★★★★}

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é)議\color{red}{★★}

  • 應(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為什么更加安全弟孟?\color{red}{★★★★}

  • 區(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的連接建立流程\color{red}{★★★}

HTTPS為了兼顧安全與效率疏叨,同時使用了對稱加密和非對稱加密潘靖。在傳輸?shù)倪^程中會涉及到三個密鑰:

  • 服務(wù)器端的公鑰和私鑰,用來進(jìn)行非對稱加密

  • 客戶端生成的隨機(jī)密鑰蚤蔓,用來進(jìn)行對稱加密

    https

如上圖卦溢,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.解釋一下 三次握手 和 四次揮手\color{red}{★★★★}

  • 三次握手

    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ū)別\color{red}{★★★}

  • TCP:面向連接玉吁、傳輸可靠(保證數(shù)據(jù)正確性,保證數(shù)據(jù)順序)、用于傳輸大量數(shù)據(jù)(流模式)腻异、速度慢进副,建立連接需要開銷較多(時間,系統(tǒng)資源)悔常。

  • UDP:面向非連接影斑、傳輸不可靠、用于傳輸少量數(shù)據(jù)(數(shù)據(jù)包模式)这嚣、速度快鸥昏。

6.Cookie和Session\color{red}{★★}

cookie

  • 1.用戶與服務(wù)器的交互

    cookie主要是用來記錄用戶狀態(tài),區(qū)分用戶姐帚,狀態(tài)保存在客戶端吏垮。cookie功能需要瀏覽器的支持。如果瀏覽器不支持cookie(如大部分手機(jī)中的瀏覽器)或者把cookie禁用了,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)就可以了。

    session
當(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是什么\color{red}{★★}

因特網(wǎng)上的主機(jī)隔心,可以使用多種方式標(biāo)識白群,比如主機(jī)名或IP地址。一種標(biāo)識方法就是用它的主機(jī)名(hostname)硬霍,比如·www.baidu.com帜慢、www.google.comgaia.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解析過程\color{red}{★★}

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ù)持久性有哪幾種?\color{red}{★★★}

iOS本地數(shù)據(jù)保存有多種方式,比如NSUserDefaults松申、歸檔、文件保存俯逾、數(shù)據(jù)庫贸桶、CoreData、KeyChain(鑰匙串)等多種方式桌肴。其中KeyChain(鑰匙串)是保存到沙盒范圍以外的地方皇筛,也就是與沙盒無關(guān)。

2.FMDB數(shù)據(jù)結(jié)構(gòu)變化升級\color{red}{★★}

  • 1.使用columnExists:inTableWithName方法判斷數(shù)據(jù)表中是否存在字段

  • 2.如果不存在坠七,則添加, 如:向bbb表中添加aaa字段 -> ALTER TABLE bbb ADD 'aaa' TEXT

多線程

1.進(jìn)程與線程\color{red}{★★★}

  • 進(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.什么是多線程撮弧?\color{red}{★★★★}

  • 多線程的實(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)\color{red}{★★★★}

  • 優(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ū)別?\color{red}{★★★★}

  • 并行:充分利用計算機(jī)的多核弊攘,在多個線程上同步進(jìn)行

  • 并發(fā):在一條線程上通過快速切換抢腐,讓人感覺在同步進(jìn)行

5.iOS中實(shí)現(xiàn)多線程的幾種方案,各自有什么特點(diǎn)襟交?\color{red}{★★★★★}

  • NSThread 面向?qū)ο蟮穆醣叮枰绦騿T手動創(chuàng)建線程,但不需要手動銷毀捣域。子線程間通信很難啼染。

  • GCD c語言,充分利用了設(shè)備的多核焕梅,自動管理線程生命周期迹鹅。比NSOperation效率更高。

  • NSOperation 基于gcd封裝丘侠,更加面向?qū)ο笸叫溃萭cd多了一些功能。

6.多個網(wǎng)絡(luò)請求完成后執(zhí)行下一步\color{red}{★★★★★}

  • 使用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í)行下一步\color{red}{★★★★★}

  • 使用信號量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í)行第二組\color{red}{★★★★★}

  • 這里使用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.多線程中的死鎖翻擒?\color{red}{★★★★★}

死鎖是由于多個線程(進(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í)行原理?\color{red}{★★★★★}

  • 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ū)別?\color{red}{★★★}

  • 核心動畫只作用在layer.
  • 核心動畫修改的值都是假像.它的真實(shí)位置沒有發(fā)生變化.
  • 當(dāng)需要與用戶進(jìn)行交互時用UIView動畫,不需要與用戶進(jìn)行交互時兩個都可以.

2.當(dāng)我們要做一些基于 CALayer 的動畫時胞得,有時需要設(shè)置 layer 的錨點(diǎn)來配合動畫荧止,這時候我們需要注意什么?\color{red}{★★★}

  • 需要注意的是設(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.圖像的壓縮方式\color{red}{★★★}

  • 壓縮圖片質(zhì)量

    一般情況下使用UIImageJPEGRepresentation或UIImagePNGRepresentation方法實(shí)現(xiàn)。

  • 壓縮圖片尺寸

    一般通過指定壓縮的大小對圖像進(jìn)行重繪

2.如何計算圖片加載內(nèi)存中所占的大小\color{red}{★★★}

圖片內(nèi)存大小的計算公式 寬度 * 高度 * bytesPerPixel/8驶悟。

bytesPerPixel : 每個像素所占的字節(jié)數(shù)。

RGBA顏色空間下 每個顏色分量由32位組成

所以一般圖片的計算公式是 wxhx4

數(shù)據(jù)安全及加密

1.對稱加密和非對稱加密的區(qū)別材失?\color{red}{★★★}

  • 1痕鳍、對稱加密又稱公開密鑰加密,加密和解密都會用到同一個密鑰,如果密鑰被攻擊者獲得笼呆,此時加密就失去了意義熊响。常見的對稱加密算法有DES、3DES诗赌、AES汗茄、Blowfish、IDEA铭若、RC5洪碳、RC6。

  • 2叼屠、非對稱加密又稱共享密鑰加密瞳腌,使用一對非對稱的密鑰,一把叫做私有密鑰镜雨,另一把叫做公有密鑰嫂侍;公鑰加密只能用私鑰來解密,私鑰加密只能用公鑰來解密荚坞。常見的公鑰加密算法有:RSA挑宠、ElGamal、背包算法颓影、Rabin(RSA的特例)各淀、迪菲-赫爾曼密鑰交換協(xié)議中的公鑰加密算法、橢圓曲線加密算法)瞭空。

2.簡述 SSL 加密的過程用了哪些加密方法揪阿,為何這么作?\color{red}{★★★}

SSL 加密咆畏,在過程中實(shí)際使用了 對稱加密 和 非對稱加密 的結(jié)合南捂。主要的考慮是先使用 非對稱加密 進(jìn)行連接,這樣做是為了避免中間人攻擊秘鑰被劫持旧找,但是 非對稱加密 的效率比較低溺健。所以一旦建立了安全的連接之后,就可以使用輕量的 對稱加密钮蛛。

3.iOS的簽名機(jī)制是怎么樣的\color{red}{★★★★}

  • 簽名機(jī)制:

    先將應(yīng)用內(nèi)容通過摘要算法鞭缭,得到摘要

    再用私鑰對摘要進(jìn)行加密得到密文

    將源文本、密文魏颓、和私鑰對應(yīng)的公鑰一并發(fā)布

  • 驗(yàn)證流程:

    查看公鑰是否是私鑰方的

    然后用公鑰對密文進(jìn)行解密得到摘要

    將APP用同樣的摘要算法得到摘要岭辣,兩個摘要進(jìn)行比對,如果相等那么一切正常

Runtime

1.Category 的實(shí)現(xiàn)原理甸饱?\color{red}{★★★★★}

  • Category 實(shí)際上是 Category_t的結(jié)構(gòu)體沦童,在運(yùn)行時仑濒,新添加的方法,都被以倒序插入到原有方法列表的最前面偷遗,所以不同的Category墩瞳,添加了同一個方法,執(zhí)行的實(shí)際上是最后一個氏豌。

  • Category 在剛剛編譯完的時候喉酌,和原來的類是分開的,只有在程序運(yùn)行起來后泵喘,通過 Runtime 泪电,Category 和原來的類才會合并到一起。

2.isa指針的理解涣旨,對象的isa指針指向哪里歪架?isa指針有哪兩種類型?\color{red}{★★★★★}

  • isa 等價于 is kind of

    實(shí)例對象的 isa 指向類對象

    類對象的 isa 指向元類對象

    元類對象的 isa 指向元類的基類

  • isa 有兩種類型

    純指針霹陡,指向內(nèi)存地址

    NON_POINTER_ISA和蚪,除了內(nèi)存地址,還存有一些其他信息

3.Objective-C 如何實(shí)現(xiàn)多重繼承烹棉?\color{red}{★★★★★}

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 屬性浆洗?\color{red}{★★★★★}

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ī)制\color{red}{★★★★}

  • 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)用\color{red}{★★★★★}

  • 利用關(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地址靖榕?\color{red}{★★★★★}

每一個類對象中都一個對象方法列表(對象方法緩存)

  • 類方法列表是存放在類對象中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)用方法的過程\color{red}{★★★★★}

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ū)別\color{red}{★★★★★}

兩者都會自動調(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)行時語言。\color{red}{★★★}

  • 主要是將數(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)系凶掰?\color{red}{★★★★★}

  • 一個線程對應(yīng)一個 Runloop燥爷。

  • 主線程的默認(rèn)就有了 Runloop。

  • 子線程的 Runloop 以懶加載的形式創(chuàng)建懦窘。

  • Runloop 存儲在一個全局的可變字典里前翎,線程是 key ,Runloop 是 value畅涂。

2.RunLoop的運(yùn)行模式\color{red}{★★★★★}

  • 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)部邏輯灯帮?\color{red}{★★★★★}

  • 實(shí)際上 RunLoop 就是這樣一個函數(shù)奠旺,其內(nèi)部是一個 do-while 循環(huán)。當(dāng)你調(diào)用 CFRunLoopRun() 時施流,線程就會一直停留在這個循環(huán)里;直到超時或被手動停止鄙信,該函數(shù)才會返回瞪醋。

    RunLoop
  • 內(nèi)部邏輯:

    1. 通知 Observer 已經(jīng)進(jìn)入了 RunLoop

    2. 通知 Observer 即將處理 Timer

    3. 通知 Observer 即將處理非基于端口的輸入源(即將處理 Source0)

    4. 處理那些準(zhǔn)備好的非基于端口的輸入源(處理 Source0)

    5. 如果基于端口的輸入源準(zhǔn)備就緒并等待處理,請立刻處理該事件装诡。轉(zhuǎn)到第 9 步(處理 Source1)

    6. 通知 Observer 線程即將休眠

    7. 將線程置于休眠狀態(tài)银受,直到發(fā)生以下事件之一

      • 事件到達(dá)基于端口的輸入源(port-based input sources)(也就是 Source0)

      • Timer 到時間執(zhí)行

      • 外部手動喚醒

      • 為 RunLoop 設(shè)定的時間超時

    8. 通知 Observer 線程剛被喚醒(還沒處理事件)

    9. 處理待處理事件

      • 如果是 Timer 事件,處理 Timer 并重新啟動循環(huán)鸦采,跳到第 2 步

      • 如果輸入源被觸發(fā)宾巍,處理該事件(文檔上是 deliver the event)

      • 如果 RunLoop 被手動喚醒但尚未超時,重新啟動循環(huán)渔伯,跳到第 2 步

4.autoreleasePool 在何時被釋放顶霞?\color{red}{★★★★★}

  • 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中的使用?\color{red}{★★★★★}

  • GCD由 子線程 返回到 主線程,只有在這種情況下才會觸發(fā) RunLoop育谬。會觸發(fā) RunLoop 的 Source 1 事件券盅。

6.AFNetworking 中如何運(yùn)用 Runloop?\color{red}{★★★}

  • 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)原理刨肃?\color{red}{★★★★}

  • 當(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:這個方法在子線程中是否起作用轻纪?\color{red}{★★★}

  • 不起作用,子線程默認(rèn)沒有 Runloop叠纷,也就沒有 Timer刻帚。可以使用 GCD的dispatch_after來實(shí)現(xiàn)

9.事件響應(yīng)的過程涩嚣?\color{red}{★★★}

  • 蘋果注冊了一個 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.手勢識別的過程舍杜?\color{red}{★★★}

  • 當(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哪個更精確\color{red}{★★★★}

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模式\color{red}{★★★★}

#MVC(Model、View亮钦、Controller)\color{red}{★★★★}

MVC是比較直觀的架構(gòu)模式馆截,最核心的就是通過Controller層來進(jìn)行調(diào)控,首先看一下官方提供的MVC示意圖:

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)\color{red}{★★★★}

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。

mvp

從上圖可以看出篮昧,從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)\color{red}{★★★★}

在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)系\color{red}{★★★}

  • 信號的依賴:使用場景是當(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ū)別侧漓?\color{red}{★★}

@weakify 可以多參數(shù)使用

4.微服務(wù)架構(gòu)設(shè)想。\color{red}{★★}

微服務(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è)計模式?\color{red}{★★★★}

  • 單例模式:單例保證了應(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.單例會有什么弊端滓窍?\color{red}{★★★}

  • 主要優(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è)計原則膝晾?\color{red}{★★★}

  • 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.組件化有什么好處啥刻?\color{red}{★★★★}

  • 業(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.你是如何組件化解耦的晋柱?\color{red}{★★★★}

  • 分層

    基礎(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的方案番挺?\color{red}{★★★}

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的組件化方案析桥,有哪些核心組成?\color{red}{★★★}

  • 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)試命令胰柑?\color{red}{★★}

  • 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)試\color{red}{★★}

  • 條件斷點(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 常見的崩潰類型有哪些竞帽?\color{red}{★★★★★}

  • 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卡頓的原因有哪些扛施?\color{red}{★★★★★}

  • 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 的流暢度?\color{red}{★★★★★}

  • 本質(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)化?\color{red}{★★★★★}

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包的大小\color{red}{★★★★★}

降低包大小需要從兩方面著手

  • 可執(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)行無損的壓縮

    去除沒有用到的資源: https://github.com/tinymind/LSUnusedResources

5.如何檢測離屏渲染與優(yōu)化\color{red}{★★★★★}

  • 檢測潜秋,通過勾選Xcode的Debug->View Debugging-->Rendering->Run->Color Offscreen-Rendered Yellow項(xiàng)蛔琅。

  • 優(yōu)化,如陰影峻呛,在繪制時添加陰影的路徑

6.怎么檢測圖層混合\color{red}{★★★}

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)存泄露斤寂?\color{red}{★★★★}

  • 目前我知道的方式有以下幾種

    Memory Leaks

    Alloctions

    Analyse

    Debug Memory Graph

    MLeaksFinder

  • 泄露的內(nèi)存主要有以下兩種:

    Laek Memory 這種是忘記 Release 操作所泄露的內(nèi)存。

    Abandon Memory 這種是循環(huán)引用邢锯,無法釋放掉的內(nèi)存。

源碼理解

1.YYKit\color{red}{★★★}

YYKit 是一組龐大搀别、功能豐富的 iOS 組件丹擎。

我們需要從組件架構(gòu),源碼歇父,設(shè)計思路蒂培,解決問題的方案策略等多方面去學(xué)習(xí)。

2.SDWebImage加載圖片過程\color{red}{★★★★}

  • 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 底層原理分析\color{red}{★★★}

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ò)展庫

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市袜蚕,隨后出現(xiàn)的幾起案子糟把,更是在濱河造成了極大的恐慌,老刑警劉巖牲剃,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件遣疯,死亡現(xiàn)場離奇詭異,居然都是意外死亡凿傅,警方通過查閱死者的電腦和手機(jī)缠犀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來聪舒,“玉大人辨液,你說我怎么就攤上這事∠洳校” “怎么了滔迈?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長被辑。 經(jīng)常有香客問我燎悍,道長,這世上最難降的妖魔是什么盼理? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任谈山,我火速辦了婚禮,結(jié)果婚禮上宏怔,老公的妹妹穿的比我還像新娘奏路。我一直安慰自己,他們只是感情好臊诊,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布思劳。 她就那樣靜靜地躺著,像睡著了一般妨猩。 火紅的嫁衣襯著肌膚如雪潜叛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天壶硅,我揣著相機(jī)與錄音威兜,去河邊找鬼。 笑死庐椒,一個胖子當(dāng)著我的面吹牛椒舵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播约谈,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼笔宿,長吁一口氣:“原來是場噩夢啊……” “哼犁钟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起泼橘,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤涝动,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后炬灭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體醋粟,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年重归,在試婚紗的時候發(fā)現(xiàn)自己被綠了米愿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡鼻吮,死狀恐怖育苟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情椎木,我是刑警寧澤违柏,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站拓哺,受9級特大地震影響勇垛,放射性物質(zhì)發(fā)生泄漏脖母。R本人自食惡果不足惜士鸥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谆级。 院中可真熱鬧烤礁,春花似錦、人聲如沸肥照。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽舆绎。三九已至鲤脏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吕朵,已是汗流浹背猎醇。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留努溃,地道東北人硫嘶。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像梧税,于是被迫代替她去往敵國和親沦疾。 傳聞我的和親對象是個殘疾皇子称近,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 設(shè)計模式是什么? 你知道哪些設(shè)計模式哮塞,并簡要敘述刨秆? 設(shè)計模式是一種編碼經(jīng)驗(yàn),就是用比較成熟的邏輯去處理某一種類型的...
    卑微的戲子閱讀 620評論 0 1
  • OC的理解與特性O(shè)C作為一門面向?qū)ο蟮恼Z言彻桃,自然具有面向?qū)ο蟮恼Z言特性:封裝坛善、繼承、多態(tài)邻眷。它既具有靜態(tài)語言的特性(...
    LIANMING_LI閱讀 511評論 0 0
  • 1.探究KVO的底層實(shí)現(xiàn)原理 http://www.reibang.com/p/829864680648 ·KV...
    大馮宇宙閱讀 2,753評論 0 3
  • ——青燈素箋 多線程的底層實(shí)現(xiàn) 1.首先搞清楚是什么線程眠屎、什么是多線程2.Mach是第一個以多線程方式處理任務(wù)的系...
    寒山半秋閱讀 1,270評論 0 19
  • 基礎(chǔ) 1.為什么說Objective-C是一門動態(tài)的語言? 所謂的動態(tài)類型語言肆饶,意思就是類型的檢查是在運(yùn)行時做的改衩。...
    陽明先生_x閱讀 1,003評論 0 17