NSRunLoop事件迴路及多線程

一、多線程

每一個iOS應用程序中都有一個主線程用來更新UI界面、處理用戶的觸摸事件、解析網絡下載的數據,因此不能把一些太耗時的操作(比如網絡下載數據)放在主線程中執行,不然會造成主線程堵塞(出現界面卡死,防止界面假死),帶來極壞的用戶體驗。
iOS的解決方案就是將那些耗時的操作放到另外一個線程中去執行,多線程異步編程是防止主線程堵塞,增加運行效率的最佳方法

異步:多個線程 同時執行
 同步:線程排隊執行
並行 —》異步
串行—》同步


多線程技術
 1、異步下載數據,是多線程技術的一個比較常見的應用場景
 2、多線程技術使用場景: app中有耗時的操作或功能(1、客戶端與服務端交互;2、從數據庫中一次性讀取大量數據 3、對大量數據的解析過程),需要在主線程之外,單獨開闢一個新的線程(子線程/工作線程)來執行


iOS所支持的多線程編程方法:

NSThread

NSOperation &  NSOperationQueue

GCD


1.怎麼創建線程
a.
// 創建一個線程 但是這個線程不會自動執行
    NSThread *t = [[NSThread alloc] initWithTarget:self selector:@selector(threadMain1:) object:n];
    // 開始運行線程 手動開啓
    [t start];
b.
// 線程創建後 就會自動運行
    [NSThread detachNewThreadSelector:@selector(threadMain0:) toTarget:self withObject:n];

2.界面假死
主線程下載數據 會造成界面卡死


3.監聽線程結束
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(threadExit:) name:NSThreadWillExitNotification object:nil];

4.多個線程之間的通信
    
    [t0 cancel];
    if ([t isCancelled]){…}
    退出/結束線程
    [NSThread exit]; // 或者是return;

5.線程鎖
NSLock 互斥鎖
避免多個線程同時修改同一資源

修改數據庫寫法

6.線程修改進度條

UI主線程刷新UI界面
[self performSelectorOnMainThread:@selector(refreshUI:) withObject:@(i) waitUntilDone:NO];

dispatch_async(dispatch_get_main_queue(), ^{
              [self  refreshUI:1];
            });
 NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(updateImage:) object:image];
        [[NSOperationQueue mainQueue] addOperation:operation];

 什麼是主線程? 凡是不是在普通/工作線程裏面的都是在 UI 主線程中

7.自定義線程
自定義的線程 要繼承與NSthread  

需要實現- main  線程主函數



擴展函數
 //獲取當前線程
 + (NSThread *)currentThread;
 //創建啓動線程
 + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
 //判斷是否是多線程
 + (BOOL)isMultiThreaded;
 
 - (NSMutableDictionary *)threadDictionary;
 //線程休眠 NSDate 給一個休眠到什麼時候
 + (void)sleepUntilDate:(NSDate *)date;
 //線程休眠時間
 + (void)sleepForTimeInterval:(NSTimeInterval)ti;
 //結束/退出當前線程
 + (void)exit;
//獲取當前線程優先級
 + (double)threadPriority;
 //設置當前線程的優先級  一般我們不設置
 //自己創建的線程優先級是 0.5  主線程是0.8左右
 //優先級返回 0---1.0
 //設置優先級
 + (BOOL)setThreadPriority:(double)p;
 //獲取指定線程的優先級
 - (double)threadPriority NS_AVAILABLE(10_6, 4_0);
 - (void)setThreadPriority:(double)p NS_AVAILABLE(10_6, 4_0);
 
 //設置線程的名字
 - (void)setName:(NSString *)n NS_AVAILABLE(10_5, 2_0);
 - (NSString *)name NS_AVAILABLE(10_5, 2_0);

 //判斷指定的線程是否是 主線程
 - (BOOL)isMainThread NS_AVAILABLE(10_5, 2_0);
 //判斷當前線程是否是主線程
 + (BOOL)isMainThread NS_AVAILABLE(10_5, 2_0); // reports whether current thread is main
 //獲取主線程
 + (NSThread *)mainThread NS_AVAILABLE(10_5, 2_0);
 
 - (id)init NS_AVAILABLE(10_5, 2_0);    // designated initializer
 - (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument NS_AVAILABLE(10_5, 2_0);
 //指定線程是否在執行
 - (BOOL)isExecuting NS_AVAILABLE(10_5, 2_0);
 //線程是否完成
 - (BOOL)isFinished NS_AVAILABLE(10_5, 2_0);
 //線程是否被取消 (是否給當前線程發過取消信號)
 - (BOOL)isCancelled NS_AVAILABLE(10_5, 2_0);
 //發送線程取消信號的 最終線程是否結束 由 線程本身決定
 - (void)cancel NS_AVAILABLE(10_5, 2_0);
 //啓動線程
 - (void)start NS_AVAILABLE(10_5, 2_0);
 
 //線程主函數  在線程中執行的函數 都要在-main函數中調用
 - (void)main NS_AVAILABLE(10_5, 2_0);    // thread body metho
 
二、任務隊列

NSOperation

NSThread 操作線程最基本的類, 得到的子線程的效率要低於NSOperation
NSOperation 是一個輕量級的線程

 NSOperationQueue 線程池,操作隊列

 以任務爲導向的管理線程機制,將操作(任務)放入到線程池裏,會自動執行,弱化線程的概念。(任務:可以認爲是線程)

自定義任務

 NSOperation 抽象類,如果想創建自己的任務,就要繼承NSOperation,來實現自己的類
 重寫main函數!


三、GCD(推薦使用)
 GCD 全稱Grand Central Dispatch(隊列調度)

 是一套低層API,提供了⼀種新的方法來進⾏併發程序編寫。
 從基本功能上講,GCD有點像NSOperationQueue,他們都允許程序將任務切分爲多個單一任務,然後提交⾄至⼯工作隊列來併發地或者串⾏行地執⾏行。

 GCD是C實現,⽐NSOpertionQueue更底層更高效,並且它不是Cocoa框架的一部分
 併發任務會像NSOperationQueue那樣基於系統負載來合適地併發進⾏,串⾏行隊列同一時間只執行單一任務
 GCD的API很大程度上基於block

1.主線程隊列
主線程隊列 內部執行任務是串行的同步操作
主線程隊列不需要我們創建,通過dispatch_get_main_queue()方法獲得
    dispatch_queue_t  queue = dispatch_get_main_queue();
 dispatch_async(queue, ^{
        [self taskThread1];
    });
或者
dispatch_async(dispatch_get_main_queue(), ^{
        [self taskThread2];
    });


2.創建私有隊列 用戶隊列/串行隊列
DISPATCH_QUEUE_SERIAL串行
// C接口,創建一個私有隊列 ,隊列名是一個C字符串,沒有特別的要求,Apple建議用倒裝的標識符來表示(這個名字,更多用於調試)
私有隊列內部也是串行操作
    dispatch_queue_t queue = dispatch_queue_create("com.qianfeng", NULL);

dispatch_async(queue, ^{
        [self taskThread2];
    });
    // 我們自己創建的隊列,我們需要自己銷燬
    //dispatch_release(queue);
非arc  需要銷燬



3.全局隊列
// 並行隊列(全局)不需要我們創建,通過dispatch_get_global_queue()方法獲得
    // 三個可用隊列
    // 第一個參數是選取按個全局隊列,一般採用DEFAULT,默認優先級隊列
    // 第二個參數是保留標誌,目前的版本沒有任何用處(不代表以後版本),直接設置爲0就可以了
    // DISPATCH_QUEUE_PRIORITY_HIGH
    // DISPATCH_QUEUE_PRIORITY_DEFAULT
    // DISPATCH_QUEUE_PRIORITY_LOW
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);


常用: 在全局隊列裏增加佔用時間很長的block(數據下載,文件操作,數據庫操作),在主隊列裏刷新UI(刷新UI,務必要在主線程進行)

NSRunLoop 事件的迴路,是ios程序中實現異步事件處理的核心,每個線程都由一個迴路來控制
currentRunLoop 拿到控制當前線程的迴路, 通過迴路來維持當前線程的活躍狀態,暫停當前線程,直到數據下載完成
while (!_isDone)  {
        //線程會阻塞
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
        NSLog(@"任務中");
        //當下載完成之後 另外一個下載線程通知當前線程,這時當前線程 來了輸入源,NSRunloop 就會退出
    } ;//只有下載完成 這種情況 纔會NSRunloop真正退出 否則其他情況使NSRunloop 退出之後 _isDone不是yes 那麼 NSRunloop 又會繼續 run

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章