百度三面被掛掉之后,沉下心來總結(jié),構(gòu)建自己的iOS開發(fā)體系(上)

在百度三面被掛掉之后,沉下心來,整理構(gòu)建自己的開發(fā)體系,方便以后查看。 金三銀四已經(jīng)降臨,為此提供了找了不少學(xué)習(xí)方向給大家,也是一些進價方向,希望能幫大家快速提升自己的短板!

本章節(jié):

標:不要浪費美好的年華,做自己覺得對的事情!

目錄

  • 一、設(shè)計原則、設(shè)計模式
  • 二、內(nèi)存管理
  • 三、多線程
  • 四、Block
  • 五、Runtime
  • 六、Runloop
  • 七、KVO
  • 八、KVC
  • 九、Category
  • 十、網(wǎng)絡(luò)
  • 十一、UI
  • 十二、其他
  • 十三、OC對象相關(guān)
2021年【最新iOS開發(fā)面試題】感謝觀看贈資料:

下載地址:

全新iOS 電子書大全 和 iOS 進階面試文檔

一、設(shè)計原則、設(shè)計模式

1、六大設(shè)計基本原則
定義:一個類只負責一件事
優(yōu)點:類的復(fù)雜度降低、可讀性增強、易維護、變更引起的風(fēng)險降低
應(yīng)用:系統(tǒng)提供的UIView和CALayer的關(guān)系:UIView負責時間傳遞、事件響應(yīng);CALayer負責動畫及展示

定義:對修改關(guān)閉、對擴展開放
- 設(shè)計的類做好后就不再修改,如果有新的需求,通過新加類的方式來滿足,而不去修改現(xiàn)有的類的代碼

優(yōu)點:靈活、穩(wěn)定(不需修改內(nèi)部代碼,使得被破壞的程度大大下降)
關(guān)鍵:抽象化

使用:
- 我們可以把把行為添加到一個協(xié)議中,使用時遵守這個協(xié)議即可。
- 添加類目(Category)方式創(chuàng)建

定義:所有引用父類的地方必須能透明地使用其子類的對象。
- 通俗點說就是,父類可以被子類無縫替換,且原有功能不受任何影響

優(yōu)點:
- 代碼共享,減少創(chuàng)建類的工作量,每個子類都擁有父類的所有屬性和方法
- 提高代碼的可重用性、擴張性,項目的開放性

缺點:程序的可移植性降低,增加了對象間的耦合性

定義:抽象不應(yīng)該依賴于具體實現(xiàn),具體實現(xiàn)可以依賴于抽象
核心思想:面向接口編程

優(yōu)點:代碼結(jié)構(gòu)清晰,維護容易
實例:平時我們使用 protocol 匿名對象模式就是依賴倒置原則的最好體現(xiàn)

定義:客戶端不應(yīng)該依賴它不需要的接口

- 使用多個專門的協(xié)議、而不是一個龐大臃腫的協(xié)議。
- 協(xié)議中的方法應(yīng)當盡量少

例:UITableViewDataSource、UITableViewDelegate
優(yōu)點:解耦、增強可讀性、可擴展性、可維護性

定義:一個對象應(yīng)該對其他對象有盡可能少的了解。
- 也就是說,如果兩個類不必彼此直接通信,那么這兩個類就不應(yīng)當發(fā)生直接的相互作用。

迪米特法則應(yīng)用:
- 外觀模式(Facade)
- 中介者模式(Mediator)
- 匿名對象

優(yōu)點:使對象之間的耦合降到最底,從而使得類具有很好的可讀性和可維護性。

特點總結(jié)

  • 單一職責原則主要說明:類的職責要單一
  • 里氏替換原則強調(diào):不要破壞繼承體系
  • 依賴倒置原則描述要:面向接口編程
  • 接口隔離原則講解:設(shè)計接口的時候要精簡
  • 迪米特法則告訴我們:要降低耦合
  • 開閉原則講述的是:對擴展開放,對修改關(guān)閉

  • 設(shè)計模式
TODO(待填充);??????????

二、內(nèi)存管理

規(guī)則

  • 在iOS中,使用 “引用計數(shù)” 來管理OC對象的內(nèi)存
  • 新創(chuàng)建的OC對象,引用計數(shù)是1;
  • 調(diào)用retain會讓OC對象的引用計數(shù)+1,調(diào)用release會讓OC對象的引用計數(shù)-1
  • 當引用計數(shù)減為0,OC對象就會銷毀,釋放占用的內(nèi)存空間
  • 當調(diào)用 alloc、new、copy、mutableCopy 方法返回了一個對象,在不需要這個對象時,要調(diào)用release或者aoturelease釋放
2、引用計數(shù)怎么存儲?
- 可以直接存儲在isa指針中
- 如果不夠存儲的話,會存儲在SideTable結(jié)構(gòu)體的refcnts散列表中

struct SideTable {
    spinlock_t stock;
    RefcountMap refcnts; // 存放著對象引用計數(shù)的散列表
    weak_table_t weak_table;
}

3、ARC具體為引用計數(shù)做了哪些工作?
- 編譯階段自動添加代碼

ARC是LLVM編譯器和Runtime系統(tǒng)相互協(xié)作的一個結(jié)果
- 編譯器幫我們實現(xiàn)內(nèi)存管理相關(guān)的代碼
- Runtime在程序運行過程中處理弱引用

4、深拷貝與淺拷貝
概念:
- 深拷貝:內(nèi)容拷貝,產(chǎn)生新的對象
- 淺拷貝:指針拷貝,沒有產(chǎn)生新的對象,原對象的引用計數(shù)+1
- 完全拷貝:深拷貝的一種,能拷貝多層內(nèi)容(使用歸解檔技術(shù))

執(zhí)行結(jié)果:
- copy:不可變拷貝,產(chǎn)生不可變副本
- mutableCopy:可變拷貝,產(chǎn)生可變副本

準則:不可變對象的copy方法是淺拷貝,其余都是深拷貝??????????
原因:
- 它是不可變對象,沒有必要拷貝一份出來,指向同一塊地址還節(jié)省內(nèi)存
- 不可變對象調(diào)用copy返回他本身,不可變對象copy就相當于是retain

1、對象的拷貝
- 遵守協(xié)議(<NSCopying, NSMutableCopying>)
- 實現(xiàn)協(xié)議方法

- (id)copyWithZone:(NSZone *)zone {
    Person *person = [Person allocWithZone:zone];
    person.name = self.name;
    return person;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
    Person *person = [Person allocWithZone:zone];
    person.name = self.name;
    return person;
}

2、集合對象的拷貝
- 對于集合類的可變對象來說,深拷貝并非嚴格意義上的深復(fù)制,只能算是單層深復(fù)制
- 即雖然新開辟了內(nèi)存地址,但是存放在內(nèi)存上的值(也就是數(shù)組里的元素仍然之鄉(xiāng)員數(shù)組元素值,并沒有另外復(fù)制一份),這就叫做單層深復(fù)制
- 對于集合類的對象如何實現(xiàn)每一層都深拷貝呢?(1、initWithArray:copyItems、2、歸檔解檔技術(shù))

#import <Foundation/Foundation.h>

@interface Person : NSObject <NSCoding>

@property (nonatomic, copy) NSString *name;

@end

#import "Person.h"

@implementation Person

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self.name = [aDecoder decodeObjectForKey:@"name"];
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:self.name forKey:@"name"];
}

@end

歸檔和解檔的概念補充:
有時存在這樣的需求,即將程序中使用的多個對象及其屬性值,以及它們的相互關(guān)系保存到文件中,或者發(fā)送給另外的進程。為了實現(xiàn)此功能,foundation框架中,可以把相互關(guān)聯(lián)的多個對象歸檔為二進制文件,而且還能將對象的關(guān)系從二進制文件中還原出來。

5、weak指針實現(xiàn)原理,SideTable的結(jié)構(gòu)是什么樣?
1、常用知識點:
- 所引用對象的計數(shù)器不會+1,并在引用對象被釋放的時候自動被設(shè)置為nil
- 通常用于解決循環(huán)引用問題

2、weak指針實現(xiàn)原理
- Runtime維護了一個weak表,用于存儲指向某個對象的所有weak指針。
- weak表其實就是一個哈希表,key:對象的內(nèi)存地址;value:指向該對象的所有弱引用的指針
- 當對象銷毀的時候,通過對象的地址值,取出對象的弱引用表,將表里面的弱引用清除

3、為什么弱引用不會導(dǎo)致循環(huán)引用?
- 沒有增加引用計數(shù)

4、SideTable的結(jié)構(gòu)是什么樣的?
struct SideTable {
    // 保證原子操作的自旋鎖
    spinlock_t slock;
    // 引用計數(shù)的 hash 表
    RefcountMap refcnts;
    // weak 引用全局 hash 表
    weak_table_t weak_table;
};

5、weak屬性如何自動置nil的? 具體到如何查找的?
TODO(待填充);??????????

6、自動釋放池相關(guān)
1、以下代碼輸出什么?會有什么問題?

for (int i = 0; i < 1000000; i ++) {
    NSString *string = @"Abc";
    string = [string lowercaseString];
    // string = [string stringByAppendingString:@"xyz"];
    string = [string stringByAppendingFormat:@"xyz"];
    NSLog(@"%d-%@", i, string);
}

問題解析:
- 每執(zhí)行一次循環(huán),就會有一個string加到當前NSRunloop中的自動釋放池中
- 只有當自動釋放池被release的時候,自動釋放池中的標示了autorelease的這些數(shù)據(jù)所占用的內(nèi)存空間才能被釋放掉
- 當someLargeNumber大到一定程度時,內(nèi)存空間將被耗盡而沒有被釋放掉,所以就出現(xiàn)了內(nèi)存溢出的現(xiàn)象。

解決方案:在循環(huán)里面加個自動釋放池
for (int i = 0; i < 1000000; i ++) {
    @autoreleasepool {
        NSString *string = @"Abc";
        string = [string lowercaseString];
        string = [string stringByAppendingFormat:@"xyz"];
        NSLog(@"%d-%@", i, string);
    }
}

2、自動釋放池底層結(jié)構(gòu):
- AutoreleasPool是通過以AutoreleasePoolPage為結(jié)點的 “雙向鏈表” 來實現(xiàn)的

3、AutoreleasPool運行的三個過程:
- objc_autoreleasePoolPush()
- [objc autorelease] 
- objc_autoreleasePoolPop(void *)

objc_autoreleasePoolPush()
- 調(diào)用的AutoreleasePoolPage的push函數(shù)
- 一個push操作其實就是創(chuàng)建一個新的Autoreleasepool
- 對應(yīng)AutoreleasePoolPage的具體實現(xiàn)就是往AutoreleasePoolPage中的next位置插入一個 POOL_SENTINEL
- 并且返回插入的 POOL_SENTINEL 的內(nèi)存地址。這個地址也就是我們前面提到的 pool token
- 在執(zhí)行 pop 操作的時候作為函數(shù)的入?yún)?
push 函數(shù)通過調(diào)用 autoreleaseFast 函數(shù)來執(zhí)行具體的插入操作
autoreleaseFast 函數(shù)在執(zhí)行具體的插入操作時三種情況不同的處理
- 當前 page 存在且沒有滿時,直接將對象添加到當前 page 中,即 next 指向的位置;
- 當前 page 存在且已滿時,創(chuàng)建一個新的 page ,并將對象添加到新創(chuàng)建的 page 中;
- 當前 page 不存在時,即還沒有 page 時,創(chuàng)建第一個 page ,并將對象添加到新創(chuàng)建的 page 中

objc_autoreleasePoolPop(void *) 函數(shù)本質(zhì)
- 就是是調(diào)用的 AutoreleasePoolPage 的 pop 函數(shù)
- pop 函數(shù)的入?yún)⒕褪?push 函數(shù)的返回值,也就是 POOL_SENTINEL 的內(nèi)存地址,即 pool token 。
- 當執(zhí)行 pop 操作時,內(nèi)存地址在 pool token 之后的所有 autoreleased 對象都會被 release 。
- 直到 pool token 所在 page 的 next 指向 pool token 為止。

TODO(待填充);??????????
4、autoreleasepool和線程的關(guān)系?

7、Copy、Strong、Weak、Assign的區(qū)別?
assign
- 用于對基本數(shù)據(jù)類型進行賦值操作,不更改引用計數(shù)
- 也可以用來修飾對象,但是被assign修飾的對象在釋放后,指針的地址還是存在的,指針并沒有被置為nil,成為野指針
- 之所以可以修飾基本數(shù)據(jù)類型,因為基本數(shù)據(jù)類型一般分配在棧上,棧的內(nèi)存會由系統(tǒng)自動處理,不會造成野指針。

weak:
- 修飾Object類型,修飾的對象在釋放后,指針地址會被置為nil,是一種弱引用
- 在ARC環(huán)境下,為避免循環(huán)引用,往往會把delegate屬性用weak修飾
- weak和strong不同的是:當一個對象不再有strong類型的指針指向它的時候,它就會被釋放,即使還有weak型指針指向它,那么這些weak型指針也將被清除。

strong:
- ARC下的strong等同于MRC下的retain都會把對象引用計數(shù)加1

copy:
- 會在內(nèi)存里拷貝一份對象,兩個指針指向不同的內(nèi)存地址。
- 一般用來修飾NSString等有對應(yīng)可變類型的對象,因為他們有可能和對應(yīng)的可變類型(NSMutableString)之間進行賦值操作,為確保可變對象變化時,對象中的字符串不被修改 ,應(yīng)該在設(shè)置屬性時拷貝一份。
- 而若用strong修飾,如果可變對象變化,對象中的字符串屬性也會跟著變化。

1、block屬性為什么需要用copy來修飾?
- 因為在MRC下,block在創(chuàng)建的時候,它的內(nèi)存是分配在棧(stack)上的,而不是在堆(heap)上,可能被隨時回收。
- 他本身的作于域是屬于創(chuàng)建時候的作用域,一旦在創(chuàng)建時候的作用域外面調(diào)用block將導(dǎo)致程序崩潰。
- 通過copy可以把block拷貝(copy)到堆,保證block的聲明域外使用。
- 在ARC下寫不寫都行,編譯器會自動對block進行copy操作。

2、代理為什么使用weak修飾?
- weak指明該對象并不負責保持delegate這個對象,delegate的銷毀由外部控制
- 如果用strong修飾,強引用后外界不能銷毀delegate對象,會導(dǎo)致循環(huán)引用

3、為什么NSMutableArray一般不用copy修飾?
- (void)setData:(NSMutableArray *)data {
    if (_data != data) {
        [_data release];
        _data = [data copy];
    }
}
拷貝完成后:可變數(shù)組->不可變數(shù)組,在外操作時(添加、刪除等)會存在問題

4、說到野指針了,什么是“僵尸對象”?
#[iOS-野指針與僵尸對象](https://www.cnblogs.com/junhuawang/p/9213093.html)?????

- 一個OC對象引用計數(shù)為0被釋放后就變成僵尸對象,僵尸對象的內(nèi)存已經(jīng)被系統(tǒng)回收
- 雖然可能該對象還存在,數(shù)據(jù)依然在內(nèi)存中,但僵尸對象已經(jīng)是不穩(wěn)定對象了,不可以再訪問或者使用
- 它的內(nèi)存是隨時可能被別的對象申請而占用的

8、- (void)dealloc底層執(zhí)行了什么?
- (void)dealloc {
    _objc_rootDealloc(self);
}

void _objc_rootDealloc(id obj) {
    ASSERT(obj);
    obj->rootDealloc();
}

inline void objc_object::rootDealloc() {
    if (isTaggedPointer()) return;  // fixme necessary?

    if (fastpath(isa.nonpointer  &&          // 無優(yōu)化過isa指針
                 !isa.weakly_referenced  &&  // 無弱引用
                 !isa.has_assoc  &&          // 無關(guān)聯(lián)對象
                 !isa.has_cxx_dtor  &&       // 無cxx析構(gòu)函數(shù)
                 !isa.has_sidetable_rc)) {   // 不存在引用計數(shù)器是否過大無法存儲在isa中(使用 sidetable 來存儲引用計數(shù))
        // 直接釋放
        assert(!sidetable_present());
        free(this);
    } else {
        // 下一步
        object_dispose((id)this);
    }
}

// 如果不能快速釋放,則調(diào)用 object_dispose()方法,做下一步的處理
static id _object_dispose(id anObject) {
    if (anObject==nil) return nil;

    objc_destructInstance(anObject);

    anObject->initIsa(_objc_getFreedObjectClass ());

    free(anObject);
    return nil;
}

void *objc_destructInstance(id obj) {
    if (obj) {
        // Read all of the flags at once for performance.
        bool cxx = obj->hasCxxDtor();               // 是否存在析構(gòu)函數(shù)
        bool assoc = obj->hasAssociatedObjects();   // 是否有關(guān)聯(lián)對象

        // This order is important.
        if (cxx) object_cxxDestruct(obj);           // 銷毀成員變量
        if (assoc) _object_remove_assocations(obj); // 釋放動態(tài)綁定的對象
        obj->clearDeallocating();
    }
    return obj;
}

/*
 * clearDeallocating一共做了兩件事
 *
 * 1、將對象弱引用表清空,即將弱引用該對象的指針置為nil
 * 2、清空引用計數(shù)表
 * - 當一個對象的引用計數(shù)值過大(超過255)時,引用計數(shù)會存儲在一個叫 SideTable 的屬性中
 * - 此時isa的 has_sidetable_rc 值為1,這就是為什么弱引用不會導(dǎo)致循環(huán)引用的原因
 */
inline void  objc_object::clearDeallocating() {
    if (slowpath(!isa.nonpointer)) {
        // Slow path for raw pointer isa.
        sidetable_clearDeallocating();
    }
    else if (slowpath(isa.weakly_referenced  ||  isa.has_sidetable_rc)) {
        // Slow path for non-pointer isa with weak refs and/or side table data.
        clearDeallocating_slow();
    }
    assert(!sidetable_present());
}


三、多線程

9、多線程 - GCD相關(guān)
GCD核心概念:「任務(wù)」、「隊列」

1、任務(wù):
- 概念:指操作,線程中執(zhí)行的那段代碼,GCD主要放在block中;
- 執(zhí)行任務(wù)的方式:「同步執(zhí)行」、「異步執(zhí)行」;
- 區(qū)別:是否等待隊列的任務(wù)執(zhí)行結(jié)束,是否具備開啟新縣城的能力;

同步執(zhí)行(sync)
- 同步添加任務(wù)到指定隊列中,在添加的任務(wù)執(zhí)行結(jié)束之前,會一直等待,直到隊列里面的任務(wù)完成之后再繼續(xù)執(zhí)行
- 只能在當前線程中執(zhí)行任務(wù),不具備開啟新線程的能力

異步執(zhí)行(async)
- 異步添加任務(wù)到指定隊列中,不會做任何等待,可以繼續(xù)執(zhí)行任務(wù)
- 可以在新的線程中執(zhí)行任務(wù),具備開啟新縣城的能力
- ??異步執(zhí)行雖然具有開啟新線程的能力,但不一定開啟新線程。(與任務(wù)指定的隊列類型有關(guān))

2、隊列(Dispatch Queue)
- 概念:執(zhí)行任務(wù)的等待隊列,即用來存放任務(wù)的隊列
- 結(jié)構(gòu):特殊的線性表,采用FIFO(先進先出)原則。即每讀取一個任務(wù),則從隊列中釋放一個任務(wù)

串行隊列:(Serial Dispatch Queue)
- 每次只有一個任務(wù)被執(zhí)行,任務(wù)依次執(zhí)行(只開啟一個線程,一個任務(wù)執(zhí)行完成后,再執(zhí)行下一個任務(wù))

并發(fā)隊列:(Concurrent Dispatch Queue)
- 可以讓多個任務(wù)并發(fā)(同時)執(zhí)行。(可以開啟多個線程,并且同時執(zhí)行任務(wù))
- ??并發(fā)隊列的「并發(fā)」功能只有在異步(dispatch_async)方法下才有效

3、GCD使用步驟
- 創(chuàng)建一個隊列(串行隊列/并發(fā)隊列)
- 將任務(wù)追加到任務(wù)的等待隊列中,然后系統(tǒng)就會根據(jù)任務(wù)類型執(zhí)行任務(wù)(同步執(zhí)行/異步執(zhí)行)

4、死鎖條件:
- 使用sync函數(shù)往當前串行隊列中添加任務(wù),會卡住當前的串行隊列。

面試題一、打印順序
NSLog(@"執(zhí)行任務(wù)1");
dispatch_queue_t queue = dispatch_queue_create("com.example.gcd.1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("com.example.gcd.2", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
    NSLog(@"執(zhí)行任務(wù)2");
    dispatch_sync(queue2, ^{
        NSLog(@"執(zhí)行任務(wù)3");
    });
    NSLog(@"執(zhí)行任務(wù)4");
});
NSLog(@"執(zhí)行任務(wù)5");

2021-03-01 16:47:46.122744+0800 ZF_Beta[17625:344152] 執(zhí)行任務(wù)1
2021-03-01 16:47:46.122977+0800 ZF_Beta[17625:344152] 執(zhí)行任務(wù)5
2021-03-01 16:47:46.122984+0800 ZF_Beta[17625:344229] 執(zhí)行任務(wù)2
2021-03-01 16:47:46.123171+0800 ZF_Beta[17625:344229] 執(zhí)行任務(wù)3
2021-03-01 16:47:46.123300+0800 ZF_Beta[17625:344229] 執(zhí)行任務(wù)4

dispatch_queue_t ser = dispatch_queue_create("ser", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
dispatch_async(ser, ^{
    NSLog(@"2");
});
NSLog(@"3");
dispatch_sync(ser, ^{
    NSLog(@"4");
});
NSLog(@"5");

2021-02-26 11:25:15.703849+0800 ZF_Beta[6156:123418] 1
2021-02-26 11:25:15.704053+0800 ZF_Beta[6156:123418] 3
2021-02-26 11:25:15.704062+0800 ZF_Beta[6156:123698] 2
2021-02-26 11:25:15.704231+0800 ZF_Beta[6156:123418] 4
2021-02-26 11:25:15.704311+0800 ZF_Beta[6156:123418] 5

- (void)viewDidLoad {
   [self performSelector:@selector(test3) withObject:nil afterDelay:0];
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"---111");
    });
    NSLog(@"---333");
}

- (void)test3 {
    NSLog(@"---222");
}

---333
---111
---222

- (void)viewDidLoad {
    [self performSelector:@selector(test1) withObject:nil afterDelay:0];
    [self performSelector:@selector(test2) withObject:nil];
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"===3");
    });
    [UIView animateWithDuration:10 animations:^{
        NSLog(@"===4");
    }];
}

- (void)test1 {
    NSLog(@"===1");
}

- (void)test2 {
    NSLog(@"===2");
}

2021-03-04 17:41:03.759310+0800 property[25604:424718] ===2
2021-03-04 17:41:03.759642+0800 property[25604:424718] ===4
2021-03-04 17:41:03.788454+0800 property[25604:424718] ===3
2021-03-04 17:41:03.789335+0800 property[25604:424718] ===1

面試題二、如何打造線程安全的NSMutableArray?
- 線程鎖:使用線程鎖在對數(shù)組讀寫時候加鎖
- 派發(fā)隊列:
《Effective Objective 2.0》中41條提出的觀點,串行同步:將讀取和寫入都安排在同一個隊列里,可保證數(shù)據(jù)同步。

面試題三、如何異步下載多張小圖最后合成一張大圖?
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{ /*加載圖片1 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片2 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片3 */ }); 
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 合并圖片
});

面試題四、什么是線程安全?
- 多線程操作過程中往往都是多個線程并發(fā)執(zhí)行的,因此同一個資源可能被多個線程同時訪問,造成資源搶奪。
- 線程安全就是多條線程同時訪問一段代碼,不會造成數(shù)據(jù)混亂的情況

面試題五、如何設(shè)置常駐線程?

面試題六、在異步線程發(fā)送通知,在主線程接收通知。會不會有什么問題?

面試題七、GCD線程是如何調(diào)度的

面試題八、如何實現(xiàn)多個任務(wù)執(zhí)行完后,再統(tǒng)一處理?
- 同步阻塞
- 柵欄函數(shù)
- 線程組

??基于runloop的線程保活、銷毀與通信:http://www.lxweimin.com/p/4d5b6fc33519

下一章:百度三面被掛掉之后,沉下心來總結(jié),構(gòu)建自己的iOS開發(fā)體系(下)

小編文章請觀看合集

文章來源作者:強子ly

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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