iOS面試個人總結(4)

內(nèi)存管理

1.什么情況使用weak關鍵字,相比assign有什么不同滔吠?

  • 什么情況使用 weak 關鍵字纲菌?

    在 ARC 中,在有可能出現(xiàn)循環(huán)引用的時候,往往要通過讓其中一端使用 weak 來解決,比如: delegate 代理屬性

    自身已經(jīng)對它進行一次強引用,沒有必要再強引用一次,此時也會使用 weak,自定義 IBOutlet 控件屬性一般也使用 weak;當然,也可以使用strong。在下文也有論述:《IBOutlet連出來的視圖屬性為什么可以被設置成weak?》

  • 不同點:

    weak 此特質(zhì)表明該屬性定義了一種“非擁有關系” (nonowning relationship)搓逾。為這種屬性設置新值時,設置方法既不保留新值矾踱,也不釋放舊值。此特質(zhì)同assign類似, 然而在屬性所指的對象遭到摧毀時,屬性值也會清空(nil out)庇麦。 而 assign 的“設置方法”只會執(zhí)行針對“純量類型” (scalar type,例如 CGFloat 或 NSlnteger 等)的簡單賦值操作喜德。

    assign 可以用非 OC 對象,而 weak 必須用于 OC 對象

2.如何讓自己的類用copy修飾符山橄?如何重寫帶copy關鍵字的setter?

  • 若想令自己所寫的對象具有拷貝功能住诸,則需實現(xiàn) NSCopying 協(xié)議驾胆。如果自定義的對象分為可變版本與不可變版本,那么就要同時實現(xiàn) NSCopying 與 NSMutableCopying 協(xié)議贱呐。

    具體步驟:

    需聲明該類遵從 NSCopying 協(xié)議

    實現(xiàn) NSCopying 協(xié)議丧诺。該協(xié)議只有一個方法:

    - (id)copyWithZone:(NSZone *)zone;
    

    注意:一提到讓自己的類用 copy 修飾符,我們總是想覆寫copy方法奄薇,其實真正需要實現(xiàn)的卻是 “copyWithZone” 方法驳阎。

  • 重寫帶 copy 關鍵字的 setter,例如:

    - (void)setName:(NSString *)name {
        //[_name release];
        _name = [name copy];
    }
    

3.深拷貝與淺拷貝

淺拷貝只是對指針的拷貝馁蒂,拷貝后兩個指針指向同一個內(nèi)存空間呵晚,深拷貝不但對指針進行拷貝,而且對指針指向的內(nèi)容進行拷貝沫屡,經(jīng)深拷貝后的指針是指向兩個不同地址的指針饵隙。

當對象中存在指針成員時,除了在復制對象時需要考慮自定義拷貝構造函數(shù)沮脖,還應該考慮以下兩種情形:

  • 當函數(shù)的參數(shù)為對象時金矛,實參傳遞給形參的實際上是實參的一個拷貝對象,系統(tǒng)自動通過拷貝構造函數(shù)實現(xiàn)勺届;

  • 當函數(shù)的返回值為一個對象時驶俊,該對象實際上是函數(shù)內(nèi)對象的一個拷貝,用于返回函數(shù)調(diào)用處免姿。

copy方法:如果是非可擴展類對象饼酿,則是淺拷貝。如果是可擴展類對象胚膊,則是深拷貝故俐。

mutableCopy方法:無論是可擴展類對象還是不可擴展類對象,都是深拷貝紊婉。

4.@property的本質(zhì)是什么购披?ivar、getter肩榕、setter是如何生成并添加到這個類中的

  • @property 的本質(zhì)是實例變量(ivar)+存取方法(access method = getter + setter),即 @property = ivar + getter + setter;

    “屬性” (property)作為 Objective-C 的一項特性刚陡,主要的作用就在于封裝對象中的數(shù)據(jù)。 Objective-C 對象通常會把其所需要的數(shù)據(jù)保存為各種實例變量株汉。實例變量一般通過“存取方法”(access method)來訪問筐乳。其中,“獲取方法” (getter)用于讀取變量值乔妈,而“設置方法” (setter)用于寫入變量值蝙云。

  • ivar、getter路召、setter 是自動合成這個類中的

    完成屬性定義后勃刨,編譯器會自動編寫訪問這些屬性所需的方法波材,此過程叫做“自動合成”(autosynthesis)。需要強調(diào)的是身隐,這個過程由編譯 器在編譯期執(zhí)行廷区,所以編輯器里看不到這些“合成方法”(synthesized method)的源代碼。除了生成方法代碼 getter贾铝、setter 之外隙轻,編譯器還要自動向類中添加適當類型的實例變量,并且在屬性名前面加下劃線垢揩,以此作為實例變量的名字玖绿。在前例中,會生成兩個實例變量叁巨,其名稱分別為 _firstName 與 _lastName斑匪。也可以在類的實現(xiàn)代碼里通過 @synthesize 語法來指定實例變量的名字.

5.@protocol和category中如何使用@property

  • 在 protocol 中使用 property 只會生成 setter 和 getter 方法聲明,我們使用屬性的目的,是希望遵守我協(xié)議的對象能實現(xiàn)該屬性

  • category 使用 @property 也是只會生成 setter 和 getter 方法的聲明,如果我們真的需要給 category 增加屬性的實現(xiàn),需要借助于運行時的兩個函數(shù):objc_setAssociatedObject和objc_getAssociatedObject

6.簡要說一下@autoreleasePool的數(shù)據(jù)結構?锋勺?

簡單說是雙向鏈表秤标,每張鏈表頭尾相接,有 parent宙刘、child指針

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

最外層池子的頂端會有一個next指針。當鏈表容量滿了悬包,就會在鏈表的頂端衙猪,并指向下一張表。

7.BAD_ACCESS在什么情況下出現(xiàn)布近?

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

8.使用CADisplayLink棵譬、NSTimer有什么注意點?

CADisplayLink预伺、NSTimer會造成循環(huán)引用订咸,可以使用YYWeakProxy或者為CADisplayLink、NSTimer添加block方法解決循環(huán)引用

9.iOS內(nèi)存分區(qū)情況

  • 棧區(qū)(Stack)

    由編譯器自動分配釋放酬诀,存放函數(shù)的參數(shù)脏嚷,局部變量的值等

    棧是向低地址擴展的數(shù)據(jù)結構,是一塊連續(xù)的內(nèi)存區(qū)域

  • 堆區(qū)(Heap)

    由程序員分配釋放

    是向高地址擴展的數(shù)據(jù)結構瞒御,是不連續(xù)的內(nèi)存區(qū)域

  • 全局區(qū)

    全局變量和靜態(tài)變量的存儲是放在一塊的父叙,初始化的全局變量和靜態(tài)變量在一塊區(qū)域,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域

    程序結束后由系統(tǒng)釋放

  • 常量區(qū)

    常量字符串就是放在這里的

    程序結束后由系統(tǒng)釋放

  • 代碼區(qū)

    存放函數(shù)體的二進制代碼

  • 注:

    • 在 iOS 中,堆區(qū)的內(nèi)存是應用程序共享的趾唱,堆中的內(nèi)存分配是系統(tǒng)負責的

    • 系統(tǒng)使用一個鏈表來維護所有已經(jīng)分配的內(nèi)存空間(系統(tǒng)僅僅記錄涌乳,并不管理具體的內(nèi)容)

    • 變量使用結束后,需要釋放內(nèi)存甜癞,OC 中是判斷引用計數(shù)是否為 0夕晓,如果是就說明沒有任何變量使用該空間,那么系統(tǒng)將其回收

    • 當一個 app 啟動后带欢,代碼區(qū)运授、常量區(qū)烤惊、全局區(qū)大小就已經(jīng)固定乔煞,因此指向這些區(qū)的指針不會產(chǎn)生崩潰性的錯誤。而堆區(qū)和棧區(qū)是時時刻刻變化的(堆的創(chuàng)建銷毀柒室,棧的彈入彈出)渡贾,所以當使用一個指針指向這個區(qū)里面的內(nèi)存時,一定要注意內(nèi)存是否已經(jīng)被釋放雄右,否則會產(chǎn)生程序崩潰(也即是野指針報錯)

10.iOS內(nèi)存管理方式

  • Tagged Pointer(小對象)

    Tagged Pointer 專門用來存儲小的對象空骚,例如 NSNumber 和 NSDate

    Tagged Pointer 指針的值不再是地址了,而是真正的值擂仍。所以囤屹,實際上它不再是一個對象了,它只是一個披著對象皮的普通變量而已逢渔。所以肋坚,它的內(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)存相關的信息)
    蘋果將 isa 設計成了聯(lián)合體,在 isa 中存儲了與該對象相關的一些內(nèi)存的信息哀蘑,原因也如上面所說诚卸,并不需要 64 個二進制位全部都用來存儲指針。

    isa 的結構:

    // x86_64 架構
    struct {
        uintptr_t nonpointer        : 1;  // 0:普通指針绘迁,1:優(yōu)化過惨险,使用位域存儲更多信息
        uintptr_t has_assoc         : 1;  // 對象是否含有或曾經(jīng)含有關聯(lián)引用
        uintptr_t has_cxx_dtor      : 1;  // 表示是否有C++析構函數(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 個二進制位存儲時脊髓,直接存儲在這里
    };
    
    // arm64 架構
    struct {
        uintptr_t nonpointer        : 1;  // 0:普通指針辫愉,1:優(yōu)化過,使用位域存儲更多信息
        uintptr_t has_assoc         : 1;  // 對象是否含有或曾經(jīng)含有關聯(lián)引用
        uintptr_t has_cxx_dtor      : 1;  // 表示是否有C++析構函數(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 個二進制位存儲時恭朗,直接存儲在這里
    };
    

    這里的 has_sidetable_rc 和 extra_rc屏镊,has_sidetable_rc 表明該指針是否引用了 sidetable 散列表,之所以有這個選項痰腮,是因為少量的引用計數(shù)是不會直接存放在 SideTables 表中的而芥,對象的引用計數(shù)會先存放在 extra_rc 中,當其被存滿時膀值,才會存入相應的 SideTables 散列表中棍丐,SideTables 中有很多張 SideTable,每個 SideTable 也都是一個散列表沧踏,而引用計數(shù)表就包含在 SideTable 之中歌逢。

  • 散列表(引用計數(shù)表、弱引用表)

    引用計數(shù)要么存放在 isa 的 extra_rc 中翘狱,要么存放在引用計數(shù)表中秘案,而引用計數(shù)表包含在一個叫 SideTable 的結構中,它是一個散列表潦匈,也就是哈希表阱高。而 SideTable 又包含在一個全局的 StripeMap 的哈希映射表中,這個表的名字叫 SideTables茬缩。

    當一個對象訪問 SideTables 時:

    • 首先會取得對象的地址赤惊,將地址進行哈希運算,與 SideTables 中 SideTable 的個數(shù)取余凰锡,最后得到的結果就是該對象所要訪問的 SideTable

    • 在取得的 SideTable 中的 RefcountMap 表中再進行一次哈希查找未舟,找到該對象在引用計數(shù)表中對應的位置

    • 如果該位置存在對應的引用計數(shù),則對其進行操作,如果沒有對應的引用計數(shù),則創(chuàng)建一個對應的 size_t 對象漾根,其實就是一個 uint 類型的無符號整型

    弱引用表也是一張哈希表的結構,其內(nèi)部包含了每個對象對應的弱引用表 weak_entry_t魂角,而 weak_entry_t 是一個結構體數(shù)組,其中包含的則是每一個對象弱引用的對象所對應的弱引用指針智绸。

11.循環(huán)引用

循環(huán)引用的實質(zhì):多個對象相互之間有強引用野揪,不能釋放讓系統(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修飾對象會被強引用,無法避免循環(huán)引用竖幔,需要手動解除板乙。

2、在合適時機去手動斷開循環(huán)引用拳氢。 通常我們使用第一種募逞。

  • 代理(delegate)循環(huán)引用屬于相互循環(huán)引用

    delegate 是iOS中開發(fā)中比較常遇到的循環(huán)引用,一般在聲明delegate的時候都要使用弱引用 weak,或者assign,當然怎么選擇使用assign還是weak馋评,MRC的話只能用assign放接,在ARC的情況下最好使用weak,因為weak修飾的變量在釋放后自動指向nil栗恩,防止野指針存在

  • NSTimer循環(huán)引用屬于相互循環(huán)使用

    在控制器內(nèi)透乾,創(chuàng)建NSTimer作為其屬性洪燥,由于定時器創(chuàng)建后也會強引用該控制器對象磕秤,那么該對象和定時器就相互循環(huán)引用了。 如何解決呢捧韵? 這里我們可以使用手動斷開循環(huán)引用: 如果是不重復定時器市咆,在回調(diào)方法里將定時器invalidate并置為nil即可。 如果是重復定時器再来,在合適的位置將其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中的對象進行持有操作,就相當于持有了其中的對象,而如果此時block中的對象又持有了該block芒篷,則會造成循環(huán)引用搜变。 解決方案就是使用__weak修飾self即可

__weak typeof(self) weakSelf = self;

self.myBlock = ^() {
        NSLog(@"%@",weakSelf.blockString);
 };

并不是所有block都會造成循環(huán)引用。 只有被強引用了的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ā)消息并不會崩潰拢军,但也沒任何效果)。 對于這種場景怔鳖,應該在block中對 對象使用__strong修飾茉唉,使得在block期間對 對象持有,block執(zhí)行結束后,解除其持有度陆。

__weak typeof(self) weakSelf = self;

self.myBlock = ^() {

        __strong __typeof(self) strongSelf = weakSelf;

        [strongSelf test];
 };

消息傳遞的方式

1.KVC實現(xiàn)原理

  • KVC魏铅,鍵-值編碼,使用字符串直接訪問對象的屬性坚芜。

  • 底層實現(xiàn)览芳,當一個對象調(diào)用setValue方法時,方法內(nèi)部會做以下操作:

    1.檢查是否存在相應key的set方法鸿竖,如果存在沧竟,就調(diào)用set方法

    2.如果set方法不存在,就會查找與key相同名稱并且?guī)聞澗€的成員屬性缚忧,如果有悟泵,則直接給成員屬性賦值

    3.如果沒有找到_key,就會查找相同名稱的屬性key闪水,如果有就直接賦值

    4.如果還沒找到糕非,則調(diào)用valueForUndefinedKey:和setValue:forUndefinedKey:方法

2.KVO的實現(xiàn)原理

KVO

KVO-鍵值觀察機制,原理如下:

  • 1.當給A類添加KVO的時候球榆,runtime動態(tài)的生成了一個子類NSKVONotifying_A朽肥,讓A類的isa指針指向NSKVONotifying_A類,重寫class方法持钉,隱藏對象真實類信息

  • 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)用始腾,這就 會記錄舊的值。而當改變發(fā)生后空执, didChangeValueForKey 會被調(diào)用浪箭,繼而 observeValueForKey:ofObject:change:context: 也會被調(diào)用。如果可以手動實現(xiàn)這些調(diào)用辨绊,就可以實現(xiàn)“手動觸發(fā)”了
    有人可能會問只調(diào)用didChangeValueForKey方法可以觸發(fā)KVO方法奶栖,其實是不能的,因為willChangeValueForKey: 記錄舊的值邢羔,如果不記錄舊的值驼抹,那就沒有改變一說了

4.通知和代理有什么區(qū)別

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

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

  • 通知耦合度低拜鹤,代理的耦合度高

5.block和delegate的區(qū)別

  • delegate運行成本低框冀,block的運行成本高

    block出棧需要將使用的數(shù)據(jù)從棧內(nèi)存拷貝到堆內(nèi)存,當然對象的話就是加計數(shù)敏簿,使用完或者block置nil后才消除明也。delegate只是保存了一個對象指針宣虾,直接回調(diào),沒有額外消耗温数。就像C的函數(shù)指針绣硝,只多做了一個查表動作。

  • delegate更適用于多個回調(diào)方法(3個以上)撑刺,block則適用于1鹉胖,2個回調(diào)時。

6.為什么Block用copy關鍵字

Block在沒有使用外部變量時够傍,內(nèi)存存在全局區(qū)甫菠,然而,當Block在使用外部變量的時候冕屯,內(nèi)存是存在于棧區(qū)寂诱,當Block copy之后,是存在堆區(qū)的安聘。存在于棧區(qū)的特點是對象隨時有可能被銷毀痰洒,一旦銷毀在調(diào)用的時候,就會造成系統(tǒng)的崩潰浴韭。所以Block要用copy關鍵字丘喻。

多線程

1.進程與線程

  • 進程:

    1.進程是一個具有一定獨立功能的程序關于某次數(shù)據(jù)集合的一次運行活動,它是操作系統(tǒng)分配資源的基本單元.

    2.進程是指在系統(tǒng)中正在運行的一個應用程序囱桨,就是一段程序的執(zhí)行過程,我們可以理解為手機上的一個app.

    3.每個進程之間是獨立的仓犬,每個進程均運行在其專用且受保護的內(nèi)存空間內(nèi)嗅绰,擁有獨立運行所需的全部資源

  • 線程

    1.程序執(zhí)行流的最小單元舍肠,線程是進程中的一個實體.

    2.一個進程要想執(zhí)行任務,必須至少有一條線程.應用程序啟動的時候,系統(tǒng)會默認開啟一條線程,也就是主線程

  • 進程和線程的關系

    1.線程是進程的執(zhí)行單元窘面,進程的所有任務都在線程中執(zhí)行

    2.線程是 CPU 分配資源和調(diào)度的最小單位

    3.一個程序可以對應多個進程(多進程),一個進程中可有多個線程,但至少要有一條線程

    4.同一個進程內(nèi)的線程共享進程資源

2.什么是多線程翠语?

  • 多線程的實現(xiàn)原理:事實上,同一時間內(nèi)單核的CPU只能執(zhí)行一個線程财边,多線程是CPU快速的在多個線程之間進行切換(調(diào)度)肌括,造成了多個線程同時執(zhí)行的假象。

  • 如果是多核CPU就真的可以同時處理多個線程了酣难。

  • 多線程的目的是為了同步完成多項任務谍夭,通過提高系統(tǒng)的資源利用率來提高系統(tǒng)的效率。

3.多線程的優(yōu)點和缺點

  • 優(yōu)點:

    能適當提高程序的執(zhí)行效率

    能適當提高資源利用率(CPU憨募、內(nèi)存利用率)

  • 缺點:

    開啟線程需要占用一定的內(nèi)存空間(默認情況下紧索,主線程占用1M,子線程占用512KB)菜谣,如果開啟大量的線程珠漂,會占用大量的內(nèi)存空間晚缩,降低程序的性能

    線程越多,CPU在調(diào)度線程上的開銷就越大

    程序設計更加復雜:比如線程之間的通信媳危、多線程的數(shù)據(jù)共享

4.多線程的 并行 和 并發(fā) 有什么區(qū)別荞彼?

  • 并行:充分利用計算機的多核,在多個線程上同步進行

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

5.iOS中實現(xiàn)多線程的幾種方案鸣皂,各自有什么特點?

  • NSThread 面向?qū)ο蟮哪乎澹枰绦騿T手動創(chuàng)建線程签夭,但不需要手動銷毀。子線程間通信很難椎侠。

  • GCD c語言第租,充分利用了設備的多核,自動管理線程生命周期我纪。比NSOperation效率更高慎宾。

  • NSOperation 基于gcd封裝,更加面向?qū)ο笄诚ぃ萭cd多了一些功能趟据。

6.多個網(wǎng)絡請求完成后執(zhí)行下一步

  • 使用GCD的dispatch_group_t

    創(chuàng)建一個dispatch_group_t

    每次網(wǎng)絡請求前先dispatch_group_enter,請求回調(diào)后再dispatch_group_leave,enter和leave必須配合使用术健,有幾次enter就要有幾次leave汹碱,否則group會一直存在。

    當所有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ù)器的一種多線程同步機制。如果semaphore計數(shù)大于等于1勘伺,計數(shù)-1跪腹,返回,程序繼續(xù)運行飞醉。如果計數(shù)為0冲茸,則等待。dispatch_semaphore_signal(semaphore)為計數(shù)+1操作,dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER)為設置等待時間缅帘,這里設置的等待時間是一直等待轴术。

    創(chuàng)建semaphore為0,等待钦无,等10個網(wǎng)絡請求都完成了逗栽,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)絡請求順序執(zhí)行后執(zhí)行下一步

  • 使用信號量semaphore

    每一次遍歷铃诬,都讓其dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)祭陷,這個時候線程會等待苍凛,阻塞當前線程,直到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柵欄方法即可實現(xiàn)

    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"第一次任務的主線程為: %@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"第二次任務的主線程為: %@", [NSThread currentThread]);
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"第一次任務, 第二次任務執(zhí)行完畢, 繼續(xù)執(zhí)行");
    });
    
    dispatch_async(queue, ^{
        NSLog(@"第三次任務的主線程為: %@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"第四次任務的主線程為: %@", [NSThread currentThread]);
    });
    

9.多線程中的死鎖兵志?

死鎖是由于多個線程(進程)在執(zhí)行過程中醇蝴,因為爭奪資源而造成的互相等待現(xiàn)象,你可以理解為卡主了想罕。產(chǎn)生死鎖的必要條件有四個:

  • 互斥條件 : 指進程對所分配到的資源進行排它性使用悠栓,即在一段時間內(nèi)某資源只由一個進程占用。如果此時還有其它進程請求資源按价,則請求者只能等待惭适,直至占有資源的進程用畢釋放。

  • 請求和保持條件 : 指進程已經(jīng)保持至少一個資源楼镐,但又提出了新的資源請求癞志,而該資源已被其它進程占有,此時請求進程阻塞框产,但又對自己已獲得的其它資源保持不放凄杯。

  • 不可剝奪條件 : 指進程已獲得的資源,在未使用完之前秉宿,不能被剝奪戒突,只能在使用完時由自己釋放。

  • 環(huán)路等待條件 : 指在發(fā)生死鎖時描睦,必然存在一個進程——資源的環(huá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有一個底層線程池铲掐,這個池中存放的是一個個的線程拾弃。之所以稱為“池”,很容易理解出這個“池”中的線程是可以重用的摆霉,當一段時間后這個線程沒有被調(diào)用胡話豪椿,這個線程就會被銷毀奔坟。注意:開多少條線程是由底層線程池決定的(線程建議控制再3~5條),池是系統(tǒng)自動來維護搭盾,不需要我們程序員來維護(看到這句話是不是很開心咳秉?)
    而我們程序員需要關心的是什么呢?我們只關心的是向隊列中添加任務鸯隅,隊列調(diào)度即可澜建。

  • 如果隊列中存放的是同步任務,則任務出隊后蝌以,底層線程池中會提供一條線程供這個任務執(zhí)行炕舵,任務執(zhí)行完畢后這條線程再回到線程池。這樣隊列中的任務反復調(diào)度跟畅,因為是同步的咽筋,所以當我們用currentThread打印的時候,就是同一條線程徊件。

  • 如果隊列中存放的是異步的任務晤硕,(注意異步可以開線程),當任務出隊后庇忌,底層線程池會提供一個線程供任務執(zhí)行舞箍,因為是異步執(zhí)行,隊列中的任務不需等待當前任務執(zhí)行完畢就可以調(diào)度下一個任務皆疹,這時底層線程池中會再次提供一個線程供第二個任務執(zhí)行疏橄,執(zhí)行完畢后再回到底層線程池中。

  • 這樣就對線程完成一個復用略就,而不需要每一個任務執(zhí)行都開啟新的線程捎迫,也就從而節(jié)約的系統(tǒng)的開銷,提高了效率表牢。在iOS7.0的時候窄绒,使用GCD系統(tǒng)通常只能開58條線程,iOS8.0以后崔兴,系統(tǒng)可以開啟很多條線程彰导,但是實在開發(fā)應用中,建議開啟線程條數(shù):35條最為合理敲茄。

Runloop

1.Runloop 和線程的關系位谋?

  • 一個線程對應一個 Runloop。

  • 主線程的默認就有了 Runloop堰燎。

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

  • Runloop 存儲在一個全局的可變字典里,線程是 key 秆剪,Runloop 是 value赊淑。

2.RunLoop的運行模式

  • RunLoop的運行模式共有5種爵政,RunLoop只會運行在一個模式下,要切換模式陶缺,就要暫停當前模式钾挟,重寫啟動一個運行模式

    - kCFRunLoopDefaultMode, App的默認運行模式,通常主線程是在這個運行模式下運行
    - UITrackingRunLoopMode, 跟蹤用戶交互事件(用于 ScrollView 追蹤觸摸滑動组哩,保證界面滑動時不受其他Mode影響)
    - kCFRunLoopCommonModes, 偽模式等龙,不是一種真正的運行模式
    - UIInitializationRunLoopMode:在剛啟動App時第進入的第一個Mode,啟動完成后就不再使用
    - GSEventReceiveRunLoopMode:接受系統(tǒng)內(nèi)部事件伶贰,通常用不到
    

3.runloop內(nèi)部邏輯蛛砰?

  • 實際上 RunLoop 就是這樣一個函數(shù),其內(nèi)部是一個 do-while 循環(huán)黍衙。當你調(diào)用 CFRunLoopRun() 時泥畅,線程就會一直停留在這個循環(huán)里;直到超時或被手動停止琅翻,該函數(shù)才會返回位仁。

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

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

    2. 通知 Observer 即將處理 Timer

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

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

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

    6. 通知 Observer 線程即將休眠

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

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

      • Timer 到時間執(zhí)行

      • 外部手動喚醒

      • 為 RunLoop 設定的時間超時

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

    9. 處理待處理事件

      • 如果是 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(即將進入Loop)揽趾,其回調(diào)內(nèi)會調(diào)用 _objc_autoreleasePoolPush() 創(chuàng)建自動釋放池。其 order 是 -2147483647苛骨,優(yōu)先級最高篱瞎,保證創(chuàng)建釋放池發(fā)生在其他所有回調(diào)之前。

  • 第二個 Observer 監(jiān)視了兩個事件: BeforeWaiting(準備進入休眠) 時調(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 中如何運用 Runloop?

  • AFURLConnectionOperation 這個類是基于 NSURLConnection 構建的,其希望能在后臺線程接收 Delegate 回調(diào)测蘑。為此 AFNetworking 單獨創(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 添加進去了碳胳。通常情況下勇蝙,調(diào)用者需要持有這個 NSMachPort (mach_port) 并在外部線程通過這個 port 發(fā)送消息到 loop 內(nèi);但此處添加 port 只是為了讓 RunLoop 不至于退出挨约,并沒有用于實際的發(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];
    }
    
  • 當需要這個后臺線程執(zhí)行任務時,AFNetworking 通過調(diào)用 [NSObject performSelector:onThread:..] 將這個任務扔到了后臺線程的 RunLoop 中诫惭。

7.PerformSelector 的實現(xiàn)原理翁锡?

  • 當調(diào)用 NSObject 的 performSelecter:afterDelay: 后,實際上其內(nèi)部會創(chuàng)建一個 Timer 并添加到當前線程的 RunLoop 中夕土。所以如果當前線程沒有 RunLoop馆衔,則這個方法會失效。

  • 當調(diào)用 performSelector:onThread: 時隘弊,實際上其會創(chuàng)建一個 Timer 加到對應的線程去哈踱,同樣的,如果對應線程沒有 RunLoop 該方法也會失效梨熙。

8.PerformSelector:afterDelay:這個方法在子線程中是否起作用开镣?

  • 不起作用,子線程默認沒有 Runloop咽扇,也就沒有 Timer邪财。可以使用 GCD的dispatch_after來實現(xiàn)

9.事件響應的過程质欲?

  • 蘋果注冊了一個 Source1 (基于 mach port 的) 用來接收系統(tǒng)事件树埠,其回調(diào)函數(shù)為 __IOHIDEventSystemClientQueueCallback()。

  • 當一個硬件事件(觸摸/鎖屏/搖晃等)發(fā)生后嘶伟,首先由 IOKit.framework 生成一個 IOHIDEvent 事件并由 SpringBoard 接收怎憋。這個過程的詳細情況可以參考這里。SpringBoard 只接收按鍵(鎖屏/靜音等),觸摸绊袋,加速毕匀,接近傳感器等幾種 Event,隨后用 mach port 轉發(fā)給需要的 App 進程癌别。隨后蘋果注冊的那個 Source1 就會觸發(fā)回調(diào)皂岔,并調(diào)用 _UIApplicationHandleEventQueue() 進行應用內(nèi)部的分發(fā)。

  • _UIApplicationHandleEventQueue() 會把 IOHIDEvent 處理并包裝成 UIEvent 進行處理或分發(fā)展姐,其中包括識別 UIGesture/處理屏幕旋轉/發(fā)送給 UIWindow 等躁垛。通常事件比如 UIButton 點擊、touchesBegin/Move/End/Cancel 事件都是在這個回調(diào)中完成的圾笨。

10.手勢識別的過程教馆?

  • 當 _UIApplicationHandleEventQueue() 識別了一個手勢時,其首先會調(diào)用 Cancel 將當前的 touchesBegin/Move/End 系列回調(diào)打斷墅拭。隨后系統(tǒng)將對應的 UIGestureRecognizer 標記為待處理活玲。

  • 蘋果注冊了一個 Observer 監(jiān)測 BeforeWaiting (Loop即將進入休眠) 事件,這個 Observer 的回調(diào)函數(shù)是 _UIGestureRecognizerUpdateObserver()谍婉,其內(nèi)部會獲取所有剛被標記為待處理的 GestureRecognizer舒憾,并執(zhí)行GestureRecognizer 的回調(diào)。

  • 當有 UIGestureRecognizer 的變化(創(chuàng)建/銷毀/狀態(tài)改變)時穗熬,這個回調(diào)都會進行相應處理镀迂。

11.CADispalyTimer和Timer哪個更精確

CADisplayLink 更精確

  • iOS設備的屏幕刷新頻率是固定的,CADisplayLink在正常情況下會在每次刷新結束都被調(diào)用唤蔗,精確度相當高探遵。

  • NSTimer的精確度就顯得低了點,比如NSTimer的觸發(fā)時間到的時候妓柜,runloop如果在阻塞狀態(tài)箱季,觸發(fā)時間就會推遲到下一個runloop周期。并且 NSTimer新增了tolerance屬性棍掐,讓用戶可以設置可以容忍的觸發(fā)的時間的延遲范圍藏雏。

  • CADisplayLink使用場合相對專一,適合做UI的不停重繪作煌,比如自定義動畫引擎或者視頻播放的渲染掘殴。NSTimer的使用范圍要廣泛的多,各種需要單次或者循環(huán)定時處理的任務都可以使用粟誓。在UI相關的動畫或者顯示內(nèi)容使用 CADisplayLink比起用NSTimer的好處就是我們不需要在格外關心屏幕的刷新頻率了奏寨,因為它本身就是跟屏幕刷新同步的。

Runtime

1.Category 的實現(xiàn)原理鹰服?

  • Category 實際上是 Category_t的結構體病瞳,在運行時,新添加的方法,都被以倒序插入到原有方法列表的最前面仍源,所以不同的Category心褐,添加了同一個方法舔涎,執(zhí)行的實際上是最后一個笼踩。

  • Category 在剛剛編譯完的時候,和原來的類是分開的亡嫌,只有在程序運行起來后嚎于,通過 Runtime ,Category 和原來的類才會合并到一起挟冠。

2.isa指針的理解于购,對象的isa指針指向哪里?isa指針有哪兩種類型知染?

  • isa 等價于 is kind of

    實例對象的 isa 指向類對象

    類對象的 isa 指向元類對象

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

  • isa 有兩種類型

    純指針肋僧,指向內(nèi)存地址

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

3.Objective-C 如何實現(xiàn)多重繼承嫌吠?

Object-c的類沒有多繼承,只支持單繼承,如果要實現(xiàn)多繼承的話,可使用如下幾種方式間接實現(xiàn)

  • 通過組合實現(xiàn)

    A和B組合掺炭,作為C類的組件

  • 通過協(xié)議實現(xiàn)

    C類實現(xiàn)A和B類的協(xié)議方法

  • 消息轉發(fā)實現(xiàn)

    forwardInvocation:方法

4.runtime 如何實現(xiàn) weak 屬性?

weak 此特質(zhì)表明該屬性定義了一種「非擁有關系」(nonowning relationship)炕矮。為這種屬性設置新值時肤视,設置方法既不持有新值(新指向的對象),也不釋放舊值(原來指向的對象)涉枫。

runtime 對注冊的類邢滑,會進行內(nèi)存布局,從一個粗粒度的概念上來講拜银,這時候會有一個 hash 表殊鞭,這是一個全局表,表中是用 weak 指向的對象內(nèi)存地址作為 key尼桶,用所有指向該對象的 weak 指針表作為 value操灿。當此對象的引用計數(shù)為 0 的時候會 dealloc,假如該對象內(nèi)存地址是 a泵督,那么就會以 a 為 key趾盐,在這個 weak 表中搜索,找到所有以 a 為鍵的 weak 對象,從而設置為 nil救鲤。

runtime 如何實現(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)建對應的弱引用表楣黍,將 weak 指針與新對象進行綁定阶女,會調(diào)用到 weak_register_no_lock秃踩。在這個過程中,為了防止多線程中競爭沖突芍秆,會有一些鎖的操作。

  • 3荆虱、釋放時:調(diào)用 clearDeallocating 函數(shù),clearDeallocating 函數(shù)首先根據(jù)對象地址獲取所有 weak 指針地址的數(shù)組菜枷,然后遍歷這個數(shù)組把其中的數(shù)據(jù)設為 nil啤誊,最后把這個 entry 從 weak 表中刪除,最后清理對象的記錄姚炕。

5.講一下 OC 的消息機制

  • OC中的方法調(diào)用其實都是轉成了objc_msgSend函數(shù)的調(diào)用钻心,給receiver(方法調(diào)用者)發(fā)送了一條消息(selector方法名)

  • objc_msgSend底層有3大階段狐史,消息發(fā)送(當前類、父類中查找)姜贡、動態(tài)方法解析、消息轉發(fā)

6.runtime具體應用

  • 利用關聯(lián)對象(AssociatedObject)給分類添加屬性

  • 遍歷類的所有成員變量(修改textfield的占位文字顏色母怜、字典轉模型、自動歸檔解檔)

  • 交換方法實現(xiàn)(交換系統(tǒng)的方法)

  • 利用消息轉發(fā)機制解決方法找不到的異常問題

  • KVC 字典轉模型

7.runtime如何通過selector找到對應的IMP地址轨域?

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

  • 類方法列表是存放在類對象中isa指針指向的元類對象中(類方法緩存)。

  • 方法列表中每個方法結構體中記錄著方法的名稱,方法實現(xiàn),以及參數(shù)類型铐然,其實selector本質(zhì)就是方法名稱,通過這個方法名稱就可以在方法列表中找到對應的方法實現(xiàn)沥阳。

  • 當我們發(fā)送一個消息給一個NSObject對象時桐罕,這條消息會在對象的類對象方法列表里查找。

  • 當我們發(fā)送一個消息給一個類時,這條消息會在類的Meta Class對象的方法列表里查找嫁怀。

8.簡述下Objective-C中調(diào)用方法的過程

Objective-C是動態(tài)語言,每個方法在運行時會被動態(tài)轉為消息發(fā)送存捺,即:objc_msgSend(receiver, selector),整個過程介紹如下:

  • objc在向一個對象發(fā)送消息時,runtime庫會根據(jù)對象的isa指針找到該對象實際所屬的類

  • 然后在該類中的方法列表以及其父類方法列表中尋找方法運行

  • 如果构韵,在最頂層的父類(一般也就NSObject)中依然找不到相應的方法時,程序在運行時會掛掉并拋出異常unrecognized selector sent to XXX

  • 但是在這之前,objc的運行時會給出三次拯救程序崩潰的機會杂数,這三次拯救程序奔潰的說明見問題《什么時候會報unrecognized selector的異炒魏停》中的說明。

9.load和initialize的區(qū)別

兩者都會自動調(diào)用父類的,不需要super操作日熬,且僅會調(diào)用一次(不包括外部顯示調(diào)用).

  • load和initialize方法都會在實例化對象之前調(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方法通常用來進行Method Swizzle崭闲,initialize方法一般用于初始化全局變量或靜態(tài)變量韧涨。

  • load和initialize方法內(nèi)部使用了鎖如孝,因此它們是線程安全的彬祖。實現(xiàn)時要盡可能保持簡單茁瘦,避免阻塞線程,不要再使用鎖储笑。

10.怎么理解Objective-C是動態(tài)運行時語言甜熔。

  • 主要是將數(shù)據(jù)類型的確定由編譯時,推遲到了運行時。這個問題其實淺涉及到兩個概念,運行時和多態(tài)突倍。

  • 簡單來說, 運行時機制使我們直到運行時才去決定一個對象的類別,以及調(diào)用該類別對象指定方法腔稀。

  • 多態(tài):不同對象以自己的方式響應相同的消息的能力叫做多態(tài)炕淮。

  • 意思就是假設生物類(life)都擁有一個相同的方法-eat;那人類屬于生物,豬也屬于生物,都繼承了life后,實現(xiàn)各自的eat,但是調(diào)用是我們只需調(diào)用各自的eat方法踩衩。也就是不同的對象以自己的方式響應了相同的消 息(響應了eat這個選擇器)叫榕。因此也可以說,運行時機制是多態(tài)的基礎.

網(wǎng)絡

1.網(wǎng)絡七層協(xié)議

  • 應用層:

    1.用戶接口、應用程序掸鹅;

    2.Application典型設備:網(wǎng)關矮嫉;

    3.典型協(xié)議、標準和應用:TELNET蹂匹、FTP、HTTP

  • 表示層:

    1.數(shù)據(jù)表示、壓縮和加密presentation

    2.典型設備:網(wǎng)關

    3.典型協(xié)議、標準和應用:ASCLL钞螟、PICT甫匹、TIFF、JPEG|MPEG

    4.表示層相當于一個東西的表示梳码,表示的一些協(xié)議暖混,比如圖片收擦、聲音和視頻MPEG辽剧。

  • 會話層:

    1.會話的建立和結束碌宴;

    2.典型設備:網(wǎng)關;

    3.典型協(xié)議上煤、標準和應用:RPC休玩、SQL、NFS楼入、X WINDOWS哥捕、ASP

  • 傳輸層:

    1.主要功能:端到端控制Transport;

    2.典型設備:網(wǎng)關嘉熊;

    3.典型協(xié)議遥赚、標準和應用:TCP、UDP阐肤、SPX

  • 網(wǎng)絡層:

    1.主要功能:路由凫佛、尋址Network;

    2.典型設備:路由器孕惜;

    3.典型協(xié)議愧薛、標準和應用:IP、IPX衫画、APPLETALK毫炉、ICMP;

  • 數(shù)據(jù)鏈路層:

    1.主要功能:保證無差錯的疏忽鏈路的data link削罩;

    2.典型設備:交換機瞄勾、網(wǎng)橋、網(wǎng)卡弥激;

    3.典型協(xié)議进陡、標準和應用:802.2、802.3ATM微服、HDLC趾疚、FRAME RELAY;

  • 物理層:

    1.主要功能:傳輸比特流Physical以蕴;

    2.典型設備:集線器糙麦、中繼器

    3.典型協(xié)議、標準和應用:V.35丛肮、EIA/TIA-232.

2.Http 和 Https 的區(qū)別喳资?Https為什么更加安全?

  • 區(qū)別

    1.HTTPS 需要向機構申請 CA 證書腾供,極少免費。

    2.HTTP 屬于明文傳輸,HTTPS基于 SSL 進行加密傳輸伴鳖。

    3.HTTP 端口號為 80节值,HTTPS 端口號為 443 。

    4.HTTPS 是加密傳輸榜聂,有身份驗證的環(huán)節(jié)搞疗,更加安全。

  • 安全

    SSL(安全套接層) TLS(傳輸層安全)

    以上兩者在傳輸層之上须肆,對網(wǎng)絡連接進行加密處理匿乃,保障數(shù)據(jù)的完整性,更加的安全豌汇。

3.HTTPS的連接建立流程

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

  • 服務器端的公鑰和私鑰拒贱,用來進行非對稱加密

  • 客戶端生成的隨機密鑰宛徊,用來進行對稱加密

    https

如上圖,HTTPS連接過程大致可分為八步:

  • 1逻澳、客戶端訪問HTTPS連接闸天。

    客戶端會把安全協(xié)議版本號、客戶端支持的加密算法列表斜做、隨機數(shù)C發(fā)給服務端苞氮。

  • 2、服務端發(fā)送證書給客戶端

    服務端接收密鑰算法配件后瓤逼,會和自己支持的加密算法列表進行比對笼吟,如果不符合,則斷開連接抛姑。否則赞厕,服務端會在該算法列表中,選擇一種對稱算法(如AES)定硝、一種公鑰算法(如具有特定秘鑰長度的RSA)和一種MAC算法發(fā)給客戶端皿桑。

    服務器端有一個密鑰對,即公鑰和私鑰蔬啡,是用來進行非對稱加密使用的诲侮,服務器端保存著私鑰,不能將其泄露箱蟆,公鑰可以發(fā)送給任何人沟绪。

    在發(fā)送加密算法的同時還會把數(shù)字證書和隨機數(shù)S發(fā)送給客戶端

  • 3、客戶端驗證server證書

    會對server公鑰進行檢查空猜,驗證其合法性绽慈,如果發(fā)現(xiàn)發(fā)現(xiàn)公鑰有問題恨旱,那么HTTPS傳輸就無法繼續(xù)。

  • 4坝疼、客戶端組裝會話秘鑰

    如果公鑰合格搜贤,那么客戶端會用服務器公鑰來生成一個前主秘鑰(Pre-Master Secret,PMS)钝凶,并通過該前主秘鑰和隨機數(shù)C仪芒、S來組裝成會話秘鑰

  • 5、客戶端將前主秘鑰加密發(fā)送給服務端

    是通過服務端的公鑰來對前主秘鑰進行非對稱加密耕陷,發(fā)送給服務端

  • 6掂名、服務端通過私鑰解密得到前主秘鑰

    服務端接收到加密信息后镶奉,用私鑰解密得到主秘鑰梯嗽。

  • 7、服務端組裝會話秘鑰

    服務端通過前主秘鑰和隨機數(shù)C秋麸、S來組裝會話秘鑰南用。

    至此膀钠,服務端和客戶端都已經(jīng)知道了用于此次會話的主秘鑰。

  • 8裹虫、數(shù)據(jù)傳輸

    客戶端收到服務器發(fā)送來的密文肿嘲,用客戶端密鑰對其進行對稱解密,得到服務器發(fā)送的數(shù)據(jù)筑公。

    同理雳窟,服務端收到客戶端發(fā)送來的密文,用服務端密鑰對其進行對稱解密匣屡,得到客戶端發(fā)送的數(shù)據(jù)封救。

4.解釋一下 三次握手 和 四次揮手

  • 三次握手

    1.由客戶端向服務端發(fā)送 SYN 同步報文。

    2.當服務端收到 SYN 同步報文之后捣作,會返回給客戶端 SYN 同步報文和 ACK 確認報文誉结。

    3.客戶端會向服務端發(fā)送 ACK 確認報文,此時客戶端和服務端的連接正式建立券躁。

  • 建立連接

    1.這個時候客戶端就可以通過 Http 請求報文惩坑,向服務端發(fā)送請求

    2.服務端接收到客戶端的請求之后,向客戶端回復 Http 響應報文也拜。

  • 四次揮手

    當客戶端和服務端的連接想要斷開的時候以舒,要經(jīng)歷四次揮手的過程,步驟如下:

    1.先由客戶端向服務端發(fā)送 FIN 結束報文慢哈。

    2.服務端會返回給客戶端 ACK 確認報文 蔓钟。此時,由客戶端發(fā)起的斷開連接已經(jīng)完成卵贱。

    3.服務端會發(fā)送給客戶端 FIN 結束報文 和 ACK 確認報文滥沫。

    4.客戶端會返回 ACK 確認報文到服務端侣集,至此,由服務端方向的斷開連接已經(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.用戶與服務器的交互

    cookie主要是用來記錄用戶狀態(tài)魁莉,區(qū)分用戶睬涧,狀態(tài)保存在客戶端。cookie功能需要瀏覽器的支持旗唁。如果瀏覽器不支持cookie(如大部分手機中的瀏覽器)或者把cookie禁用了畦浓,cookie功能就會失效。


    cookie

    a).首次訪問amazon時检疫,客戶端發(fā)送一個HTTP請求到服務器端 讶请。服務器端發(fā)送一個HTTP響應到客戶端,其中包含Set-Cookie頭部

    b).客戶端發(fā)送一個HTTP請求到服務器端屎媳,其中包含Cookie頭部夺溢。服務器端發(fā)送一個HTTP響應到客戶端

    c).隔段時間再去訪問時,客戶端會直接發(fā)包含Cookie頭部的HTTP請求烛谊。服務器端發(fā)送一個HTTP響應到客戶端

  • 2.cookie的修改和刪除

    在修改cookie的時候风响,只需要新cookie覆蓋舊cookie即可,在覆蓋的時候丹禀,由于Cookie具有不可跨域名性状勤,注意name、path湃崩、domain需與原cookie一致

    刪除cookie也一樣荧降,設置cookie的過期時間expires為過去的一個時間點,或者maxAge = 0(Cookie的有效期,單位為秒)即可

  • 3攒读、cookie的安全

    事實上朵诫,cookie的使用存在爭議,因為它被認為是對用戶隱私的一種侵害薄扁,而且cookie并不安全

    HTTP協(xié)議不僅是無狀態(tài)的剪返,而且是不安全的废累。使用HTTP協(xié)議的數(shù)據(jù)不經(jīng)過任何加密就直接在網(wǎng)絡上傳播,有被截獲的可能脱盲。使用HTTP協(xié)議傳輸很機密的內(nèi)容是一種隱患邑滨。

a).如果不希望Cookie在HTTP等非安全協(xié)議中傳輸,可以設置Cookie的secure屬性為true钱反。瀏覽器只會在HTTPS和SSL等安全協(xié)議中傳輸此類Cookie掖看。

b).此外,secure屬性并不能對Cookie內(nèi)容加密面哥,因而不能保證絕對的安全性哎壳。如果需要高安全性,需要在程序中對Cookie內(nèi)容加密尚卫、解密归榕,以防泄密。

c).也可以設置cookie為HttpOnly吱涉,如果在cookie中設置了HttpOnly屬性刹泄,那么通過js腳本將無法讀取到cookie信息,這樣能有效的防止XSS(跨站腳本攻擊)攻擊

Session

  • Session是服務器端使用的一種記錄客戶端狀態(tài)的機制怎爵,使用上比Cookie簡單一些特石,相應的也增加了服務器的存儲壓力。

  • Session是另一種記錄客戶狀態(tài)的機制疙咸,不同的是Cookie保存在客戶端瀏覽器中县匠,而Session保存在服務器上。
    客戶端瀏覽器訪問服務器的時候撒轮,服務器把客戶端信息以某種形式記錄在服務器上乞旦。這就是Session√馍剑客戶端瀏覽器再次訪問時只需要從該Session中查找該客戶的狀態(tài)就可以了兰粉。

    session
  • 如圖:

    當程序需要為某個客戶端的請求創(chuàng)建一個session時,服務器首先檢查這個客戶端的請求里是否已包含了一個session標識(稱為SessionId)

    如果已包含則說明以前已經(jīng)為此客戶端創(chuàng)建過session顶瞳,服務器就按照SessionId把這個session檢索出來玖姑,使用(檢索不到,會新建一個)

    如果客戶端請求不包含SessionId慨菱,則為此客戶端創(chuàng)建一個session并且生成一個與此session相關聯(lián)的SessionId焰络,SessionId的值應該是一個既不會重復,又不容易被找到規(guī)律以仿造的字符串符喝,這個SessionId將被在本次響應中返回給客戶端保存闪彼。

    保存這個SessionId的方式可以采用cookie,這樣在交互過程中瀏覽器可以自動的按照規(guī)則把這個標識發(fā)送給服務器。但cookie可以被人為的禁止畏腕,則必須有其他機制以便在cookie被禁止時仍然能夠把SessionId傳遞回服務器缴川。

Cookie 和Session 的區(qū)別:

  • 1、cookie數(shù)據(jù)存放在客戶的瀏覽器上描馅,session數(shù)據(jù)放在服務器上把夸。

  • 2、cookie相比session不是很安全铭污,別人可以分析存放在本地的cookie并進行cookie欺騙,考慮到安全應當使用session恋日。

  • 3、session會在一定時間內(nèi)保存在服務器上嘹狞。當訪問增多谚鄙,會比較占用你服務器的性能,考慮到減輕服務器性能方面,應當使用cookie刁绒。

  • 4、單個cookie保存的數(shù)據(jù)不能超過4K烤黍,很多瀏覽器都限制一個站點最多保存20個cookie知市。而session存儲在服務端,可以無限量存儲

  • 5速蕊、所以:將登錄信息等重要信息存放為session;其他信息如果需要保留嫂丙,可以放在cookie中

7.DNS是什么

因特網(wǎng)上的主機,可以使用多種方式標識规哲,比如主機名或IP地址跟啤。一種標識方法就是用它的主機名(hostname),比如·www.baidu.com唉锌、www.google.com隅肥、gaia.cs.umass.edu等。這方式方便人們記憶和接受袄简,但是這種長度不一腥放、沒有規(guī)律的字符串路由器并不方便處理。還有一種方式绿语,就是直接使用定長的秃症、有著清晰層次結構的IP地址,路由器比較熱衷于這種方式吕粹。為了折衷這兩種方式种柑,我們需要一種能進行主機名到IP地址轉換的目錄服務。這就是域名系統(tǒng)(Domain Name System匹耕,DNS)的主要任務聚请。

  • DNS是:

    1、一個由分層的DNS服務器實現(xiàn)的分布式數(shù)據(jù)庫

    2泌神、一個使得主機能夠查詢分布式數(shù)據(jù)庫的應用層協(xié)議

  • DNS服務器通常是運行BIND軟件的UNIX機器良漱,DNS協(xié)議運行在UDP上舞虱,使用53號端口

  • DNS通常是由其他應用層協(xié)議所使用的,包括HTTP母市、SMTP等矾兜。其作用則是:將用戶提供的主機名解析為IP地址

  • DNS的一種簡單設計就是在因特網(wǎng)上只使用一個DNS服務器,該服務器包含所有的映射患久。很明顯這種設計是有很大的問題的:

    單點故障:如果該DNS服務器崩潰椅寺,全世界的網(wǎng)絡隨之癱瘓

    通信容量:單個DNS服務器必須處理所有DNS查詢

    遠距離的集中式數(shù)據(jù)庫:單個DNS服務器必須面對所有用戶,距離過遠會有嚴重的時延蒋失。

    維護:該數(shù)據(jù)庫過于龐大返帕,還需要對新添加的主機頻繁更新。

所以篙挽,DNS被設計成了一個分布式荆萤、層次數(shù)據(jù)庫

8.DNS解析過程

www.163.com為例:

  • 客戶端打開瀏覽器,輸入一個域名铣卡。比如輸入www.163.com链韭,這時,客戶端會發(fā)出一個DNS請求到本地DNS服務器煮落。本地DNS服務器一般都是你的網(wǎng)絡接入服務器商提供敞峭,比如中國電信,中國移動蝉仇。

  • 查詢www.163.com的DNS請求到達本地DNS服務器之后旋讹,本地DNS服務器會首先查詢它的緩存記錄,如果緩存中有此條記錄轿衔,就可以直接返回結果沉迹。如果沒有,本地DNS服務器還要向DNS根服務器進行查詢害驹。

  • 根DNS服務器沒有記錄具體的域名和IP地址的對應關系胚股,而是告訴本地DNS服務器,你可以到域服務器上去繼續(xù)查詢裙秋,并給出域服務器的地址琅拌。

  • 本地DNS服務器繼續(xù)向域服務器發(fā)出請求,在這個例子中摘刑,請求的對象是.com域服務器进宝。.com域服務器收到請求之后,也不會直接返回域名和IP地址的對應關系枷恕,而是告訴本地DNS服務器党晋,你的域名的解析服務器的地址。

  • 最后,本地DNS服務器向域名的解析服務器發(fā)出請求未玻,這時就能收到一個域名和IP地址對應關系灾而,本地DNS服務器不僅要把IP地址返回給用戶電腦,還要把這個對應關系保存在緩存中扳剿,以備下次別的用戶查詢時旁趟,可以直接返回結果,加快網(wǎng)絡訪問庇绽。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末锡搜,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子瞧掺,更是在濱河造成了極大的恐慌耕餐,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辟狈,死亡現(xiàn)場離奇詭異肠缔,居然都是意外死亡,警方通過查閱死者的電腦和手機哼转,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門桩砰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人释簿,你說我怎么就攤上這事∨鹈В” “怎么了庶溶?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長懂鸵。 經(jīng)常有香客問我偏螺,道長,這世上最難降的妖魔是什么匆光? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任套像,我火速辦了婚禮,結果婚禮上终息,老公的妹妹穿的比我還像新娘夺巩。我一直安慰自己,他們只是感情好周崭,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布柳譬。 她就那樣靜靜地躺著,像睡著了一般续镇。 火紅的嫁衣襯著肌膚如雪美澳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機與錄音制跟,去河邊找鬼舅桩。 笑死,一個胖子當著我的面吹牛雨膨,可吹牛的內(nèi)容都是我干的擂涛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼哥放,長吁一口氣:“原來是場噩夢啊……” “哼歼指!你這毒婦竟也來了?” 一聲冷哼從身側響起甥雕,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤踩身,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后社露,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挟阻,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年峭弟,在試婚紗的時候發(fā)現(xiàn)自己被綠了附鸽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡瞒瘸,死狀恐怖坷备,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情情臭,我是刑警寧澤省撑,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站俯在,受9級特大地震影響竟秫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜跷乐,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一肥败、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧愕提,春花似錦馒稍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至仗颈,卻和暖如春佛舱,著一層夾襖步出監(jiān)牢的瞬間椎例,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工请祖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留订歪,地道東北人。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓肆捕,卻偏偏與公主長得像刷晋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子慎陵,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

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

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,100評論 1 32
  • 面向?qū)ο蟮娜筇卣?并作簡單的介紹眼虱。 面向?qū)ο蟮娜齻€基本特征是:封裝、繼承席纽、多態(tài)捏悬。 1.封裝是面向?qū)ο蟮奶卣髦?...
    xiny123閱讀 1,432評論 0 6
  • 把網(wǎng)上的一些結合自己面試時遇到的面試題總結了一下,以后有新的還會再加進來润梯。 1. OC 的理解與特性 OC 作為一...
    AlaricMurray閱讀 2,564評論 0 20
  • 抗清名將袁崇煥被奸臣陷害过牙,其舊部盡力撫養(yǎng)遺孤袁承志,并聚集圣峰嶂纺铭,立志復仇寇钉。不料事情敗露,袁承志死里逃生舶赔,在一個啞...
    扁羽閱讀 745評論 1 2