iOS開發之多線程技術—GCD篇

本篇將從四個方面對iOS開發中GCD的使用進行詳盡的講解:程序員


1、什麼是GCD

2、咱們爲何要用GCD技術

3、在實際開發中如何使用GCD更好的實現咱們的需求

  1、Synchronous & Asynchronous 同步 & 異步

  2、Serial Queues & Concurrent Queues 串行 & 併發

  3、Global Queues全局隊列

  4、Main Queue主隊列

  5、同步的做用

  6、dispatch_time延遲操做

  7、線程安全(單例dispatch_once、讀寫dispatch_barrier_async)

  8、調度組(dispatch_group)

4、定時源事件和子線程的運行循環


1、什麼是GCD

  GCD 是基於 C 的 API,它是 libdispatch 的市場名稱,而 libdispatch 做爲 Apple 的一個庫,爲併發代碼在多核硬件(跑 iOS 或 OS X )上執行提供有力支持。數組

 


2、咱們爲何要用GCD技術

  • GCD 能經過推遲昂貴計算任務並在後臺運行它們來改善你的應用的響應性能。
  • GCD 提供一個易於使用的併發模型而不只僅只是鎖和線程,以幫助咱們避開併發陷阱。
  • GCD 具備在常見模式(例如單例)上用更高性能的原語優化你的代碼的潛在能力。
  • GCD旨在替換NSThread等線程技術
  • GCD可充分利用設備的多核
  • GCD可自動管理線程的生命週期

 


3、在實際開發中如何使用GCD更好的實現咱們的需求

1、Synchronous & Asynchronous 同步 & 異步

1)同步任務執行方式:在當前線程中執行,必須等待當前語句執行完畢,纔會執行下一條語句安全

 

#pragma mark
#pragma mark - 同步方法
/**
 同步的打印順序
 打印 begin
 打印 [NSThread currentThread]
 打印 end
 */
- (void)syncTask {
    
    NSLog(@"begin");
    
    // 1.GCD同步方法
    /**
     參數1:隊列 第一個參數0其實爲隊列優先級DISPATCH_QUEUE_PRIORITY_DEFAULT,若是要適配 iOS 7.0 & 8.0,則始終爲0
     參數2:任務
     */
    dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        // 任務中要執行的代碼
        NSLog(@"%@", [NSThread currentThread]);
        
    });
    
    NSLog(@"end");
    
}
同步方法

 

2)異步任務執行方式:不在當前線程中執行,不用等待當前語句執行完畢,就能夠執行下一條語句網絡

 

#pragma mark
#pragma mark - 異步方法
/**
 異步的打印順序
 打印 begin
 打印 通常狀況下爲end,極少數狀況下會很快開闢完新的線程,先打印出[NSThread currentThread]
 */
- (void)asyncTask {
    
    /**
     異步:不會在「當前線程」執行,會首先去開闢新的子線程,開闢線程須要花費時間
     */
    
    NSLog(@"begin");
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@", [NSThread currentThread]);
    });
    
    NSLog(@"end");
    
}
異步方法

 

2、Serial Queues & Concurrent Queues 串行 & 併發

1)串行隊列調度同步和異步任務執行多線程

串行隊列特色:
  以先進先出的方式,順序調度隊列中的任務執行
  不管隊列中所指定的執行任務函數是同步仍是異步,都會等待前一個任務執行完成後,再調度後面的任務併發

 

#pragma mark
#pragma mark - 串行隊列同步方法
/**
 串行隊列,同步方法
 1.打印順序 : 從上到下,依次打印,由於是串行的
 2.在哪條線程上執行 : 主線程,由於是同步方法,因此在當前線程裏面執行,剛好當前線程是主線程,因此它就在主線程上面執行
 
 應用場景:開發中不多用
 */
- (void)serialSync {
    // 1.建立一個串行隊列
    /**
     參數1:隊列的表示符號,通常是公司的域名倒寫
     參數2:隊列的類型
         DISPATCH_QUEUE_SERIAL 串行隊列
         DISPATCH_QUEUE_CONCURRENT 併發隊列
     */
    dispatch_queue_t serialQuene = dispatch_queue_create("com.baidu", DISPATCH_QUEUE_SERIAL);
    
    // 建立任務
    void (^task1) () = ^() {
        NSLog(@"task1---%@", [NSThread currentThread]);
    };
    
    void (^task2) () = ^() {
        NSLog(@"task2---%@", [NSThread currentThread]);
    };
    
    void (^task3) () = ^() {
        NSLog(@"task3---%@", [NSThread currentThread]);
    };
    
    // 添加任務到隊列,同步方法執行
    dispatch_sync(serialQuene, task1);
    dispatch_sync(serialQuene, task2);
    dispatch_sync(serialQuene, task3);
}
串行隊列同步方法

 

#pragma mark
#pragma mark - 串行隊列異步方法
/**
 串行隊列,異步方法
 1.打印順序:從上到下,依次執行,它是串行隊列
 2.在哪條線程上執行:在子線程,由於它是異步執行,異步就是不在當前線程裏面執行
 
 應用場景:耗時間,有順序的任務
    1.登陸--->2.付費--->3.才能看
 
 */
- (void)serialAsync {
    // 除了第三步,和串行同步方法中都是同樣的
    // 1.建立一個串行隊列
    dispatch_queue_t serialQuene = dispatch_queue_create("com.baidu", DISPATCH_QUEUE_SERIAL);
    
    // 2.建立任務
    void (^task1)() = ^() {
        NSLog(@"task1---%@", [NSThread currentThread]);
    };
    void (^task2)() = ^() {
        NSLog(@"task2---%@", [NSThread currentThread]);
    };
    void (^task3)() = ^() {
        NSLog(@"task3---%@", [NSThread currentThread]);
    };
    
    // 3.添加任務到隊列
    dispatch_async(serialQuene, task1);
    dispatch_async(serialQuene, task2);
    dispatch_async(serialQuene, task3);
}
串行隊列異步方法

 

2)併發隊列調度異步任務執行框架

併發隊列特色:
  以先進先出的方式,併發調度隊列中的任務執行
  若是當前調度的任務是同步執行的,會等待任務執行完成後,再調度後續的任務
  若是當前調度的任務是異步執行的,同時底層線程池有可用的線程資源,會再新的線程調度後續任務的執行異步

 

#pragma mark
#pragma mark - 併發隊列同步任務
/**
 併發隊列,同步任務
 1.打印順序:由於是同步,因此依次執行
 2.在哪條線程上執行:主線程,由於它是同步方法,它就在當前線程裏面執行,也就是在主線程裏面依次執行
 
 當併發隊列遇到同步的時候仍是依次執行,因此說方法(同步/異步)的優先級會比隊列的優先級高
 
 * 只要是同步方法,都只會在當前線程裏面執行,不會開子線程
 
 應用場景:
    開發中幾乎不用
 
 */
- (void)serialSync {
    
    /**
     參數1:隊列的表示符號,通常是公司的域名倒寫
     參數2:隊列的類型
     DISPATCH_QUEUE_SERIAL 串行隊列
     DISPATCH_QUEUE_CONCURRENT 併發隊列
     */
    
    // 1.建立併發隊列
    dispatch_queue_t serialSync = dispatch_queue_create("com.xiaojukeji", DISPATCH_QUEUE_CONCURRENT);
    
    // 2.建立任務
    void (^task1)() = ^() {
        NSLog(@"task1---%@", [NSThread currentThread]);
    };
    
    void (^task2)() = ^() {
        NSLog(@"task2---%@", [NSThread currentThread]);
    };
    
    void (^task3)() = ^() {
        NSLog(@"task3---%@", [NSThread currentThread]);
    };
    
    // 3.添加任務到併發隊列
    dispatch_sync(serialSync, task1);
    dispatch_sync(serialSync, task2);
    dispatch_sync(serialSync, task3);
}
併發隊列同步任務
#pragma mark
#pragma mark - 併發隊列異步任務
/**
 1.打印順序:無序的
 2.在哪條線程上執行:在子線程上執行,每個任務都在它本身的線程上執行
        能夠建立N條子線程,它是由底層可調度線程池來決定的,可調度線程池它是有一個重用機制
 
 應用場景
    同時下載多個影片
 */
- (void)serialAsync {
    // 1.建立併發隊列
    dispatch_queue_t serialAsync = dispatch_queue_create("com.xiaojukeji", DISPATCH_QUEUE_CONCURRENT);
    
    // 2.建立任務
    void (^task1)() = ^() {
        NSLog(@"task1---%@", [NSThread currentThread]);
    };
    
    void (^task2)() = ^() {
        NSLog(@"task2---%@", [NSThread currentThread]);
    };
    
    void (^task3)() = ^() {
        NSLog(@"task3---%@", [NSThread currentThread]);
    };
    
    // 3.將任務添加到併發隊列
    dispatch_async(serialAsync, task1);
    dispatch_async(serialAsync, task2);
    dispatch_async(serialAsync, task3);
}
併發隊列異步任務

 

3、全局隊列

全局隊列是系統爲了方便程序員開發提供的,其工做表現與併發隊列一致async

全局隊列 & 併發隊列的區別ide

  全局隊列:沒有名稱,不管 MRC & ARC 都不須要考慮釋放,平常開發中,建議使用"全局隊列"
  併發隊列:有名字,和 NSThread 的 name 屬性做用相似,若是在 MRC 開發時,須要使用 dispatch_release(q); 釋放相應的對象
  dispatch_barrier 必須使用自定義的併發隊列
  開發第三方框架時,建議使用併發隊列

參數
  參數1:服務質量(隊列對任務調度的優先級)/iOS 7.0 以前,是優先級

iOS 8.0(新增,暫時不能用,今年年末)
QOS_CLASS_USER_INTERACTIVE 0x21, 用戶交互(但願最快完成-不能用太耗時的操做)
QOS_CLASS_USER_INITIATED 0x19, 用戶指望(但願快,也不能太耗時)
QOS_CLASS_DEFAULT 0x15, 默認(用來底層重置隊列使用的,不是給程序員用的)
QOS_CLASS_UTILITY 0x11, 實用工具(專門用來處理耗時操做!)
QOS_CLASS_BACKGROUND 0x09, 後臺
QOS_CLASS_UNSPECIFIED 0x00, 未指定,能夠和iOS 7.0 適配
iOS 7.0
DISPATCH_QUEUE_PRIORITY_HIGH 2 高優先級
DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默認優先級
DISPATCH_QUEUE_PRIORITY_LOW (-2) 低優先級
DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 後臺優先級

  參數2:爲將來保留使用的,應該永遠傳入0

  結論:若是要適配 iOS 7.0 & 8.0,使用如下代碼: dispatch_get_global_queue(0, 0);

 

#pragma mark
#pragma mark - 全局隊列同步任務
/**
 全局隊列,同步任務
 1.打印順序:依次執行,由於它是同步的
 2.在哪條線程上執行:主線程,由於它是同步方法,它就在當前線程裏面執行
 
 當它遇到同步的時候,併發隊列仍是依次執行,因此說,方法的優先級比隊列的優先級高
 
 * 只要是同步方法,都只會在當前線程裏面執行,不會開子線程
 
 應用場景:開發中幾乎不用
 */
- (void)globalSync {
    /**
     參數1:
        IOS7:表示的優先級
        IOS8:服務質量
        爲了保證兼容IOS7&IOS8通常傳入0
     
     參數2:將來使用,傳入0
     */
    
    NSLog(@"begin");
    // 1.建立全局隊列
    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
    
    // 2.建立任務
    void (^task1)() = ^() {
        NSLog(@"task1----%@", [NSThread currentThread]);
    };
    
    void (^task2)() = ^() {
        NSLog(@"task2----%@", [NSThread currentThread]);
    };
    
    void (^task3)() = ^() {
        NSLog(@"task3----%@", [NSThread currentThread]);
    };
    
    // 3.添加任務到全局隊列
    dispatch_sync(globalQueue, task1);
    dispatch_sync(globalQueue, task2);
    dispatch_sync(globalQueue, task3);

    NSLog(@"end");
}
全局隊列同步任務
#pragma mark
#pragma mark - 全局隊列異步任務
/**
 全局隊列,異步方法
 1.打印順序:無序的
 2.在子線程上執行,每個任務都在它本身的線程上執行,線程數由底層可調度線程池來決定的,可調度線程池有一個重用機制
 應用場景:
    蜻蜓FM同時下載多個聲音
 */
- (void)globalAsync {
    
    NSLog(@"begin");
    
    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
    
    void (^task1)() = ^() {
        NSLog(@"task1---%@", [NSThread currentThread]);
    };
    
    void (^task2)() = ^() {
        NSLog(@"task2---%@", [NSThread currentThread]);
    };
    
    void (^task3)() = ^() {
        NSLog(@"task3---%@", [NSThread currentThread]);
    };
    
    dispatch_async(globalQueue, task1);
    dispatch_async(globalQueue, task2);
    dispatch_async(globalQueue, task3);
    
    NSLog(@"end");
    
}
全局隊列異步任務

 

4、主隊列

特色
  專門用來在主線程上調度任務的隊列
  不會開啓線程
  以先進先出的方式,在主線程空閒時纔會調度隊列中的任務在主線程執行
  若是當前主線程正在有任務執行,那麼不管主隊列中當前被添加了什麼任務,都不會被調度

隊列獲取
  主隊列是負責在主線程調度任務的
  會隨着程序啓動一塊兒建立
  主隊列只須要獲取不用建立

 

#pragma mark
#pragma mark - 主隊列異步任務
/**
 主隊列,異步任務
 1.執行順序:依次執行,由於它在主線程裏面執行
 * 彷佛與咱們的異步任務有所衝突,可是由於它是主隊列,因此,只在主線程裏面執行
 
 2.是否會開線程:不會,由於它在主線程裏面執行
 
 應用場景:
    當作了耗時操做以後,咱們須要回到主線程更新UI的時候,就非它不可
 */
- (void)mainAsync {
    
    NSLog(@"begin");
    
    // 1.建立主隊列
    dispatch_queue_t mainAsync = dispatch_get_main_queue();
    
    // 2.建立任務
    void (^task1)() = ^() {
        NSLog(@"task1---%@", [NSThread currentThread]);
    };
    
    void (^task2)() = ^() {
        NSLog(@"task2---%@", [NSThread currentThread]);
    };
    
    void (^task3)() = ^() {
        NSLog(@"task3---%@", [NSThread currentThread]);
    };

    dispatch_async(mainAsync, task1);
    dispatch_async(mainAsync, task2);
    dispatch_async(mainAsync, task3);
    
    NSLog(@"end");
    
}
主隊列異步任務
#pragma mark
#pragma mark - 主隊列同步方法有問題,不能用是個奇葩,會形成死鎖
/**
 主隊列,同步任務有問題,不能用,彼此都在等對方是否執行完了,因此是互相死等
 主隊列只有在主線程空閒的時候,纔會去調度它裏面的任務去執行
 */
- (void)mainSync {
    
    NSLog(@"begin");
    // 1.建立主隊列
    dispatch_queue_t mainSync = dispatch_get_main_queue();
    
    // 2.建立任務
    void (^task1)() = ^() {
        NSLog(@"task1---%@", [NSThread currentThread]);
    };
    
    void (^task2)() = ^() {
        NSLog(@"task2---%@", [NSThread currentThread]);
    };
    
    void (^task3)() = ^() {
        NSLog(@"task3---%@", [NSThread currentThread]);
    };
    
    // 3.添加任務到主隊列中
    dispatch_sync(mainSync, task1);
    dispatch_sync(mainSync, task2);
    dispatch_sync(mainSync, task3);
    
    NSLog(@"end");
}
主隊列同步方法有問題,不能用是個奇葩,會形成死鎖

 

Deadlock 死鎖

  兩個(有時更多)東西——在大多數狀況下,是線程——所謂的死鎖是指它們都卡住了,並等待對方完成或執行其它操做。第一個不能完成是由於它在等待第二個的完成。但第二個也不能完成,由於它在等待第一個的完成。

 

5、同步的做用

同步任務,可讓其餘異步執行的任務,依賴某一個同步任務,例如:在用戶登陸以後,才容許異步下載文件!

#pragma mark
#pragma mark - 模擬登陸下載多個電影數據
/**
 同步的做用:保證咱們任務執行的前後順序
 1.登陸
 
 2.同時下載三部電影
 */
- (void)loadManyMovie {
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        NSLog(@"%@", [NSThread currentThread]);
        // 1.登陸,同步在當前線程裏面工做
        dispatch_sync(dispatch_get_global_queue(0, 0), ^{
            
            NSLog(@"登陸了---%@", [NSThread currentThread]);
            sleep(3);
            
        });
        
        // 2.同時下載三部電影()
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"正在下載第一個電影---%@", [NSThread currentThread]);
        });
        
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"正在下載第二個電影---%@", [NSThread currentThread]);
        });
        
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"正在下載第三個電影---%@", [NSThread currentThread]);
        });
        
        dispatch_sync(dispatch_get_main_queue(), ^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"計算機將在三秒後關閉---%@", [NSThread currentThread]);
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"關機了---%@", [NSThread currentThread]);
            });
        });
        
    });
    
}
模擬登陸下載多個電影數據

 

6、dispatch_time延遲操做

不知道什麼時候適合使用 dispatch_after ?

  • 自定義串行隊列:在一個自定義串行隊列上使用 dispatch_after 要當心。你最好堅持使用主隊列。
  • 主隊列(串行):是使用 dispatch_after 的好選擇;Xcode 提供了一個不錯的自動完成模版。
  • 併發隊列:在併發隊列上使用 dispatch_after 也要當心;你會這樣作就比較罕見。仍是在主隊列作這些操做吧。
// MARK: - 延遲執行
- (void)delay {
    /**
     從如今開始,通過多少納秒,由"隊列"調度異步執行 block 中的代碼

     參數
     1. when    從如今開始,通過多少納秒
     2. queue   隊列
     3. block   異步執行的任務
     */
    dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC));
    void (^task)() = ^ {
        NSLog(@"%@", [NSThread currentThread]);
    };
    // 主隊列
//    dispatch_after(when, dispatch_get_main_queue(), task);
    // 全局隊列
//    dispatch_after(when, dispatch_get_global_queue(0, 0), task);
    // 串行隊列
    dispatch_after(when, dispatch_queue_create("itheima", NULL), task);

    NSLog(@"come here");
}

- (void)after {
    [self.view performSelector:@selector(setBackgroundColor:) withObject:[UIColor orangeColor] afterDelay:1.0];

    NSLog(@"come here");
}
延遲執行

 

7、線程安全(單例dispatch_once、讀寫dispatch_barrier_async)

  一個常見的擔心是它們經常不是線程安全的。這個擔心十分合理,基於它們的用途:單例經常被多個控制器同時訪問。

  單例的線程擔心範圍從初始化開始,到信息的讀和寫。

  dispatch_once() 以線程安全的方式執行且僅執行其代碼塊一次。試圖訪問臨界區(即傳遞給 dispatch_once 的代碼)的不一樣的線程會在臨界區已有一個線程的狀況下被阻塞,直到臨界區完成爲止。

// 使用 dispatch_once 實現單例
+ (instancetype)sharedSingleton {
    static id instance;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });

    return instance;
}
使用 dispatch_once 實現單例

  線程安全實例不是處理單例時的惟一問題。若是單例屬性表示一個可變對象,那麼你就須要考慮是否那個對象自身線程安全。

  若是問題中的這個對象是一個 Foundation 容器類,那麼答案是——「極可能不安全」!Apple 維護一個有用且有些心寒的列表,衆多的 Foundation 類都不是線程安全的。如:NSMutableArray。

  雖然許多線程能夠同時讀取 NSMutableArray 的一個實例而不會產生問題,但當一個線程正在讀取時讓另一個線程修改數組就是不安全的。在目前的情況下不能預防這種狀況的發生。GCD 經過用 dispatch barriers 建立一個讀者寫者鎖,提供了一個優雅的解決方案。

 

8、調度組(dispatch_group)

#pragma mark
#pragma mark - 調度組
/**
 調度組的實現原理:相似引用計數器進行+1和-1的操做
 應用場景
 好比同時開了三個線程下載視頻,只有當三個視頻徹底下載完畢後,我才能作後續的事
 這個就須要用到調度組,這個調度組,就能監聽它裏面的任務是否都執行完畢
 */
- (void)groupDispatch {
    
    // 1.建立調度組
    dispatch_group_t group = dispatch_group_create();
    
    // 2.獲取全局隊列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    // 3.建立三個下載任務
    void (^task1) () = ^(){
        NSLog(@"%@----下載片頭",[NSThread currentThread]);
    };
    
    dispatch_group_enter(group); // 引用計數+1
    void (^task2) () = ^(){
        NSLog(@"%@----下載中間的內容",[NSThread currentThread]);
        
        [NSThread sleepForTimeInterval:3.0];
        
        NSLog(@"--下載中間內容完畢---");
        dispatch_group_leave(group); // 引用計數-1
    };
    
    dispatch_group_enter(group); // 引用計數+1
    void (^task3) () = ^(){
        NSLog(@"%@----下載片尾",[NSThread currentThread]);
        dispatch_group_leave(group); // 引用計數-1
    };
    
    // 4.須要將咱們的隊列 和 任務,加入到組內去監控
    dispatch_group_async(group, queue, task1);
    dispatch_group_async(group, queue, task2);
    dispatch_group_async(group, queue, task3);
    
    // 5.監聽的函數
    /**
     遠離:來監聽當調度組的引用計數器爲0時,纔會執行該函數中內容,不然不會執行
     參數1:組
     參數2:決定了參數3在哪一個線程裏面執行
     參數3:組內徹底下載完畢後須要執行的代碼
     */
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 表示組內的全部內容所有下載完成後會來到這裏
        NSLog(@"把下好的視頻按照順序拼接好,而後顯示在UI去播放%@", [NSThread currentThread]);
    });
    
}
調度組

 

1.由於你在使用的是同步的 dispatch_group_wait ,它會阻塞當前線程,因此你要用 dispatch_async 將整個方法放入後臺隊列以免阻塞主線程。

2.建立一個新的 Dispatch Group,它的做用就像一個用於未完成任務的計數器。
3.dispatch_group_enter 手動通知 Dispatch Group 任務已經開始。你必須保證 dispatch_group_enter 和 dispatch_group_leave 成對出現,不然你可能會遇到詭異的崩潰問題。
4.手動通知 Group 它的工做已經完成。再次說明,你必需要確保進入 Group 的次數和離開 Group 的次數相等。
5.dispatch_group_wait 會一直等待,直到任務所有完成或者超時。若是在全部任務完成前超時了,該函數會返回一個非零值。你能夠對此返回值作條件判斷以肯定是否超出等待週期;然而,你在這裏用 DISPATCH_TIME_FOREVER 讓它永遠等待。它的意思,勿庸置疑就是,永-遠-等-待!這樣很好,由於圖片的建立工做老是會完成的。
6.此時此刻,你已經確保了,要麼全部的圖片任務都已完成,要麼發生了超時。而後,你在主線程上運行 completionBlock 回調。這會將工做放到主線程上,並在稍後執行。
7.最後,檢查 completionBlock 是否爲 nil,若是不是,那就運行它。
編譯並運行你的應用,嘗試下載多個圖片,觀察你的應用是在什麼時候運行 completionBlock 的。

注意:若是你是在真機上運行應用,並且網絡活動發生得太快以至難以觀察 completionBlock 被調用的時刻,那麼你能夠在 Settings 應用裏的開發者相關部分裏打開一些網絡設置,以確保代碼按照咱們所指望的那樣工做。只需去往 Network Link Conditioner 區,開啓它,再選擇一個 Profile,「Very Bad Network」 就不錯。
若是你是在模擬器裏運行應用,你可使用 來自 GitHub 的 Network Link Conditioner 來改變網絡速度。它會成爲你工具箱中的一個好工具,由於它強制你研究你的應用在鏈接速度並不是最佳的狀況下會變成什麼樣。

 


4、定時源事件和子線程的運行循環

 1 - (void)viewDidLoad {
 2     [super viewDidLoad];
 3     
 4     NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeEvent) userInfo:nil repeats:YES];
 5     
 6     [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
 7     
 8 }
 9 
10 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
11     [self performSelectorInBackground:@selector(subThreadRun) withObject:nil];
12 }
13 
14 #pragma mark
15 #pragma mark - 子線程的運行循環
16 - (void)subThreadRun {
17     
18     NSLog(@"%@----%s", [NSThread currentThread], __func__);
19     
20     // 1.定義一個定時器
21     NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeEvent) userInfo:nil repeats:YES];
22     
23     // 2.將咱們的定時器加入到運行循環,只有加入到當前的運行循環裏面去,他才知道你這個時候,有一個定時任務
24     /**
25      NSDefaultRunLoopMode 當拖動的時候,它會停掉
26      由於這種模式是互斥的
27      forMode:UITrackingRunLoopMode 只有輸入的時候,它纔會去執行定時器任務
28      
29      NSRunLoopCommonModes 包含了前面兩種
30      
31     //[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
32     //[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
33      */
34     [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
35     
36     // 下載、定時源時間、輸入源時間,若是放在子線程裏面,若是想要它執行任務,就必須開啓子線程的運行循環
37     CFRunLoopRun();
38     
39 }
40 
41 - (void)timeEvent {
42     
43     NSLog(@"%d----%@", self.count, [NSThread currentThread]);
44     
45     if (self.count++ == 10) {
46         NSLog(@"---掛了----");
47         // 中止當前的運行循環
48         CFRunLoopStop(CFRunLoopGetCurrent());
49     }
50     
51 }

  

在完成過程當中,http://www.raywenderlich.com/60749/grand-central-dispatch-in-depth-part-1 感謝這的提示

相關文章
相關標籤/搜索