iOS筆記_12_多線程

來源:互聯網
上載者:User

標籤:

主線程
  • 一個iOS程式運行後,預設會開啟1條線程,稱為“主線程”或“UI線程”(重新整理UI介面最好在主線程中做,在子線程中可能會出現莫名其妙的BUG)
  • 主線程的作用
    • 顯示\重新整理UI介面
    • 處理UI事件(比如點擊事件、滾動事件、拖拽事件等)
  • 注意點
    • 別將比較耗時的操作放到主線程中
    • 耗時操作會卡住主線程,嚴重影響UI的流暢度,給使用者一種“卡”的壞體驗
  • iOS中多線程的實現方案
    • pthread(c語言,程式員管理)
      • 一套通用的多線程API
      • 適用於Unix\Linux\Windows等系統
      • 跨平台\可移植
      • 使用難度大
    • NSThread(oc語言,程式員管理)
      • 使用更加物件導向
      • 簡單易用,可直接操作線程對象
    • GCD(c語言,自動管理)
      • 旨在替代NSThread等線程技術
      • 充分利用裝置的多核
    • NSOperation(oc語言,自動管理)
      • 基於GCD(底層是GCD)
      • 比GCD多了一些更簡單實用的功能
      • 使用更加物件導向
NSThread
  • 一個NSThread對象就代表一條線程
  • 建立、啟動線程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];[thread start];// 線程一啟動,就會線上程thread中執行self的run方法
  • 常見相關用法
+ (NSThread *)mainThread; // 獲得主線程- (BOOL)isMainThread; // 是否為主線程+ (BOOL)isMainThread; // 是否為主線程// 擷取當前線程NSThread *current = [NSThread currentThread];// 線程的名字- (void)setName:(NSString *)n;- (NSString *)name;
  • 其他建立線程方式
    • 優點:簡單快捷
    • 缺點:無法對線程進行更詳細的設定
// 建立線程後自動啟動線程[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];// 隱式建立並啟動線程[self performSelectorInBackground:@selector(run) withObject:nil];
  • 控制線程的狀態
// 啟動線程- (void)start; // 進入就緒狀態 -> 運行狀態。當線程任務執行完畢,自動進入死亡狀態// 阻塞(暫停)線程+ (void)sleepUntilDate:(NSDate *)date;+ (void)sleepForTimeInterval:(NSTimeInterval)ti;// 進入阻塞狀態// 強制停止線程+ (void)exit;// 進入死亡狀態// 注意:一旦線程停止(死亡)了,就不能再次開啟任務
互斥鎖
  • 互斥鎖使用格式
@synchronized(鎖對象) { // 需要鎖定的代碼  }// 注意:鎖定1份代碼只用1把鎖,用多把鎖是無效的
原子和非原子屬性
  • OC在定義屬性時有nonatomic和atomic兩種選擇
    • atomic:原子屬性,為setter方法加鎖(預設就是atomic)
    • nonatomic:非原子屬性,不會為setter方法加鎖
  • nonatomic和atomic對比
    • atomic:安全執行緒,需要消耗大量的資源
    • nonatomic:非安全執行緒,適合記憶體小的行動裝置
  • iOS開發的建議
    • 所有屬性都聲明為nonatomic
    • 盡量避免多線程搶奪同一塊資源
    • 盡量將加鎖、資源搶奪的商務邏輯交給伺服器端處理,減小移動用戶端的壓力
線程間的通訊
  • 什麼叫做線程間通訊
    • 在1個進程中,線程往往不是孤立存在的,多個線程之間需要經常進行通訊
  • 線程間通訊的體現
    • 1個線程傳遞資料給另1個線程
    • 在1個線程中執行完特定任務後,轉到另1個線程繼續執行任務
  • 線程間通訊常用方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
GCD
  • 本質及優點
    • 全稱是Grand Central Dispatch,可譯為“牛逼的中樞調度器”
    • GCD會自動管理線程的生命週期(建立線程、調度任務、銷毀線程)
    • 程式員只需要告訴GCD想要執行什麼任務,不需要編寫任何線程管理代碼
  • 使用GCD的步驟
    • 定製任務
      • 確定想做的事情
    • 將任務添加到隊列中
      • GCD會自動將隊列中的任務取出,放到對應的線程中執行
      • 任務的取出遵循隊列的FIFO原則:先進先出,後進後出
  • 執行任務常用的函數
// 用同步的方式執行任務dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);// queue:隊列// block:任務// 注意:使用sync函數往當前串列隊列中新增工作,會卡住當前的串列隊列。同步方式會阻塞當前隊列。// 用非同步方式執行任務dispatch_async(dispatch_queue_t queue, dispatch_block_t block);dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);// 在前面的任務執行結束後它才執行,而且它後面的任務等它執行完成之後才會執行// 注意:這個queue不能是全域的並發隊列
  • 同步和非同步區別
    • 同步:只能在當前線程中執行任務,不具備開啟新線程的能力
    • 非同步:可以在新的線程中執行任務,具備開啟新線程的能力
  • 隊列的類型
    • 並發隊列(Concurrent Dispatch Queue)
      • 可以讓多個任務並發(同時)執行(自動開啟多個線程同時執行任務)
      • 並發功能只有在非同步(dispatch_async)函數下才有效
    • 串列隊列(Serial Dispatch Queue)
      • 讓任務一個接著一個地執行(一個任務執行完畢後,再執行下一個任務)
  • 並發隊列
// 建立並發隊列dispatch_queue_t queue = dispatch_queue_create("隊列名稱", DISPATCH_QUEUE_CONCURRENT)// 第一個參數 const char *label 隊列名稱 // 第二個參數 dispatch_queue_attr_t attr 隊列的類型 DISPATCH_QUEUE_CONCURRENT 表示並發隊列// 獲得全域的並發隊列dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 第一個參數 dispatch_queue_priority_t priority 隊列的優先順序// 第二個參數 unsigned long flags 此參數暫時無用,用0即可#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 預設(中)#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 後台
  • 串列隊列
// 建立串列隊列(隊列類型傳遞NULL或者DISPATCH_QUEUE_SERIAL)dispatch_queue_t queue = dispatch_queue_create("隊列名稱", NULL); // 獲得主隊列,主隊列也是串列隊列dispatch_queue_t queue = dispatch_get_main_queue();
  • 線程間的通訊
// 從子線程回到主線程dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    // 執行耗時的非同步作業...      dispatch_async(dispatch_get_main_queue(), ^{        // 回到主線程,執行UI重新整理操作        });});
  • 延遲操作
    • iOS中延遲操作有3種
      • 調用NSObject的方法
      • 使用GCD函數
      • 使用NSTimer
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];// 2秒後再調用self的run方法dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{    // 2秒後執行這裡的代碼...});[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:NO];
  • 一次性代碼
// 使用dispatch_once函數能保證某段代碼在程式運行過程中只被執行1次static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{    // 只執行1次的代碼(這裡面預設是安全執行緒的)});
  • 快速迭代
// 使用dispatch_apply函數能進行快速迭代遍曆dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index){    // 執行10次代碼,index順序不確定});
  • 隊列組
    • 可以滿足的一種需求(當然也可以用依賴線程依賴實現,下文會提到)
      • 首先:分別非同步執行2個耗時的操作
      • 其次:等2個非同步作業都執行完畢後,再回到主線程執行操作
dispatch_group_t group =  dispatch_group_create();dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    // 執行1個耗時的非同步作業});dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    // 執行1個耗時的非同步作業});dispatch_group_notify(group, dispatch_get_main_queue(), ^{    // 等前面的非同步作業都執行完畢後,回到主線程...});
單例模式
  • 單例模式的作用
    • 可以保證在程式運行過程,一個類只有一個執行個體,而且該執行個體易於供外界訪問
    • 從而方便地控制了執行個體個數,並節約系統資源
  • 單例模式的使用場合
    • 在整個應用程式中,共用一份資源(這份資源只需要建立初始化1次)
  • 實現步驟:
// 在.m中保留一個全域的static的執行個體static id _instance;// 重寫allocWithZone:方法,在這裡建立唯一的執行個體(注意安全執行緒),alloc方法會調用allocWithZone:方法+ (instancetype)allocWithZone:(struct _NSZone *)zone{    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        _instance = [super allocWithZone:zone];    });    return _instance;}// 提供1個類方法讓外界訪問唯一的執行個體+ (instancetype)sharedInstance{    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        _instance = [[self alloc] init];    });    return _instance;}// 實現copyWithZone:方法- (id)copyWithZone:(struct _NSZone *)zone{    return _instance;}
NSOperation
  • NSOperation和NSOperationQueue實現多線程的具體步驟
    • 先將需要執行的操作封裝到一個NSOperation對象中
    • 然後將NSOperation對象添加到NSOperationQueue中
    • 系統會自動將NSOperationQueue中的NSOperation取出來
    • 將取出的NSOperation封裝的操作放到一條新線程中執行
  • NSOperation是抽象類別,使用NSOperation子類的方式有3種
    • NSInvocationOperation
    • NSBlockOperation
    • 自訂子類繼承NSOperation,實現內部相應的方法
  • NSInvocationOperation
// 建立NSInvocationOperation對象- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;// 調用start方法開始執行操作- (void)start;// 一旦執行操作,就會調用target的sel方法// 注意:預設情況下,調用了start方法後並不會開一條新線程去執行操作,而是在當前線程同步執行操作。只有將NSOperation放到一個NSOperationQueue中,才會非同步執行操作
  • NSBlockOperation
// 建立NSBlockOperation對象+ (id)blockOperationWithBlock:(void (^)(void))block;// 通過addExecutionBlock:方法添加更多的操作- (void)addExecutionBlock:(void (^)(void))block;// 注意:只要NSBlockOperation封裝的運算元 > 1,就會非同步執行操作
  • NSOperationQueue
// 添加操作到NSOperationQueue中- (void)addOperation:(NSOperation *)op;- (void)addOperationWithBlock:(void (^)(void))block;
  • 相關操作
// 設定最大並發數- (NSInteger)maxConcurrentOperationCount;- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;// 取消隊列的所有操作- (void)cancelAllOperations;// 提示:也可以調用NSOperation的- (void)cancel方法取消單個操作// 暫停和恢複隊列- (void)setSuspended:(BOOL)b; // YES代表暫停隊列,NO代表恢複隊列- (BOOL)isSuspended;
  • 操作依賴
    • NSOperation之間可以設定依賴來保證執行順序
    • 可以在不同queue的NSOperation之間建立依賴關係
[operationB addDependency:operationA]; // 操作B依賴於操作A
  • 操作的監聽
// 可以監聽一個操作的執行完畢- (void (^)(void))completionBlock;- (void)setCompletionBlock:(void (^)(void))block;
  • 自訂NSOperation
    • 只需要重寫- (void)main方法,在裡面實現想執行的任務
    • 注意點
      • 自己建立自動釋放池(因為如果是非同步作業,無法訪問主線程的自動釋放池)
      • 經常通過- (BOOL)isCancelled方法檢測操作是否被取消,對取消做出響應
計算時間差
NSDate *start = [NSDate date];// 上下兩句代碼間放需要計算時間的程式NSDate *end = [NSDate date];NSLog(@"%f", [end timeIntervalSinceDate:start]);
CFTimeInterval begin = CFAbsoluteTimeGetCurrent();// 上下兩句代碼間放需要計算時間的程式CFTimeInterval end = CFAbsoluteTimeGetCurrent();NSLog(@"%f", end - begin);
小結:

1、在GCD中,方法如果用非同步函數可以開啟子線程做事情,該方法中的程式會順序執行到底,然後再返回去開啟子線程執行內部的操作。如果是同步函數,則不能開啟子線程,裡面的同步函數只能一個一個執行下去。
2、如果在主隊列中調用同步函數,容易造成死結。

iOS筆記_12_多線程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.