主線程處理UI,避免耗時操做程序員
iOS多線程技術有4種服務器
pthread,通用技術,跨平臺 c語言,程序員管理生命週期,幾乎不用多線程
NSThread 面向對象,能夠直接操做線程 OC語言 程序員 管理生命週期,偶爾使用併發
GCD 替代NSThread,能夠利用多核性能 c語言 自動管理 常用異步
NSOperation 基於GCD,更加面向對象,多了功能 OC語言 自動管理 常用async
得到主隊列函數
// 1.得到主隊列 dispatch_queue_t queue = dispatch_get_main_queue();
得到全局併發隊列性能
// 1.得到全局的併發隊列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
GCD其餘用法atom
dispatch_once
dispatch_after
dispatch_group_async\dispatch_group_notify
url
異步函數,串行隊列
/** * 用dispatch_async異步函數往串行隊列中添加任務 */ - (void)asyncSerialQueue { // 1.建立串行隊列 dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", NULL); // 2.添加任務到隊列中 執行 dispatch_async(queue, ^{ NSLog(@"----下載圖片1-----%@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"----下載圖片2-----%@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"----下載圖片3-----%@", [NSThread currentThread]); }); // 總結: 只開1個線程執行任務 }
異步函數,併發隊列
/** * 用dispatch_sync同步函數往併發隊列中添加任務 */ - (void)syncGlobalQueue { // 1.得到全局的併發隊列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 2.添加任務到隊列中 執行 dispatch_sync(queue, ^{ NSLog(@"----下載圖片1-----%@", [NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"----下載圖片2-----%@", [NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"----下載圖片3-----%@", [NSThread currentThread]); }); // 總結: 不會開啓新的線程, 併發隊列失去了併發的功能 }
同步函數,串行隊列
/** * 用dispatch_sync同步函數往串行列中添加任務 */ - (void)syncSerialQueue { // 1.建立串行隊列 dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", NULL); // 2.添加任務到隊列中 執行 dispatch_sync(queue, ^{ NSLog(@"----下載圖片1-----%@", [NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"----下載圖片2-----%@", [NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"----下載圖片3-----%@", [NSThread currentThread]); }); // 3.釋放資源 // dispatch_release(queue); // MRC(非ARC) // 總結: 不會開啓新的線程 }
同步函數,串行隊列
/** * 用dispatch_sync同步函數往串行列中添加任務 */ - (void)syncSerialQueue { // 1.建立串行隊列 dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", NULL); // 2.添加任務到隊列中 執行 dispatch_sync(queue, ^{ NSLog(@"----下載圖片1-----%@", [NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"----下載圖片2-----%@", [NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"----下載圖片3-----%@", [NSThread currentThread]); }); // 總結: 不會開啓新的線程 }
GCD一個例子
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ NSLog(@"--download--%@", [NSThread currentThread]); // 下載圖片 NSURL *url = [NSURL URLWithString:@"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"]; NSData *data = [NSData dataWithContentsOfURL:url]; // 這行會比較耗時 UIImage *image = [UIImage imageWithData:data]; // 回到主線程顯示圖片 dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"--imageView--%@", [NSThread currentThread]); self.imageView.image = image; }); });
GCD多個任務構建一個組
完成2個圖片的下載之後,進行合併處理
//定義宏簡化調用 #define global_queue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) #define main_queue dispatch_get_main_queue() // 建立一個組 dispatch_group_t group = dispatch_group_create(); // 開啓一個任務下載圖片1 __block UIImage *image1 = nil; dispatch_group_async(group, global_queue, ^{ image1 = [self imageWithURL:@"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"]; }); // 開啓一個任務下載圖片2 __block UIImage *image2 = nil; dispatch_group_async(group, global_queue, ^{ image2 = [self imageWithURL:@"http://news.baidu.com/z/resource/r/image/2014-06-22/b2a9cfc88b7a56cfa59b8d09208fa1fb.jpg"]; }); // 同時執行下載圖片1\下載圖片2操做 // 等group中的全部任務都執行完畢, 再回到主線程執行其餘操做 dispatch_group_notify(group, main_queue, ^{ self.imageView1.image = image1; self.imageView2.image = image2; // 合併 UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 100), NO, 0.0); [image1 drawInRect:CGRectMake(0, 0, 100, 100)]; [image2 drawInRect:CGRectMake(100, 0, 100, 100)]; self.bigImageView.image = UIGraphicsGetImageFromCurrentImageContext(); // 關閉上下文 UIGraphicsEndImageContext(); });
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil]; self.thread.name = @"線程A"; [self.thread start];
[self performSelectorInBackground:@selector(download) withObject:nil];
下面選一個就好了
// 2.回到主線程顯示圖片 [self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO]; // setImage: 1s [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO]; [self performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:NO];
解決多個線程同時改變同一個變量的時候的資源搶奪問題,而後變量的數值會不正確
@synchronized
- (void)viewDidLoad { [super viewDidLoad]; // 默認有100張 self.leftTicketsCount = 100; // 開啓多條線程同時賣票 self.thread0 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; self.thread0.name = @"售票員 A"; // self.thread0.threadPriority = 0.0; self.thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; self.thread1.name = @"售票員 B"; // self.thread1.threadPriority = 1.0; self.thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; self.thread2.name = @"售票員 C"; // self.thread2.threadPriority = 0.0; } /** * 賣票 */ - (void)saleTicket { while (1) { @synchronized(self) { // 加鎖(只能用一把鎖) // 1.先檢查票數 int count = self.leftTicketsCount; if (count > 0) { // 暫停 // [NSThread sleepForTimeInterval:0.0002]; // 2.票數 - 1 self.leftTicketsCount = count - 1; NSThread *current = [NSThread currentThread]; NSLog(@"%@ 賣了一張票, 剩餘%d張票", current.name, self.leftTicketsCount); } else { // 退出線程 [NSThread exit]; } } // 解鎖 } } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self.thread0 start]; [self.thread1 start]; [self.thread2 start]; }
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil]; NSBlockOperation *operation2 = [[NSBlockOperation alloc] init]; [operation2 addExecutionBlock:^{ NSLog(@"NSBlockOperation------下載圖片1---%@", [NSThread currentThread]); }]; NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{ for (int i = 0; i < 10; i++) { NSLog(@"NSBlockOperation------下載圖片---%@", [NSThread currentThread]); [NSThread sleepForTimeInterval:0.1]; } }];
// 2.建立隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; queue.maxConcurrentOperationCount = 2; // 2 ~ 3爲宜 // 設置依賴 [operation2 addDependency:operation3]; [operation3 addDependency:operation1]; // 3.添加操做到隊列中(自動執行操做, 自動開啓線程) [queue addOperation:operation1]; [queue addOperation:operation2]; [queue addOperation:operation3];
//頭文件設計 #import <Foundation/Foundation.h> @class HMDownloadOperation; //設置代理 @protocol HMDownloadOperationDelegate <NSObject> @optional - (void)downloadOperation:(HMDownloadOperation *)operation didFinishDownload:(UIImage *)image; @end @interface HMDownloadOperation : NSOperation @property (nonatomic, copy) NSString *url; @property (nonatomic, strong) NSIndexPath *indexPath; @property (nonatomic, weak) id<HMDownloadOperationDelegate> delegate; @end
實現文件
主要須要實現main方法,而後在主線程中將處理結果返回給代理
#import "HMDownloadOperation.h" @implementation HMDownloadOperation /** * 在main方法中實現具體操做 */ - (void)main { @autoreleasepool { NSURL *downloadUrl = [NSURL URLWithString:self.url]; NSData *data = [NSData dataWithContentsOfURL:downloadUrl]; // 這行會比較耗時 UIImage *image = [UIImage imageWithData:data]; if ([self.delegate respondsToSelector:@selector(downloadOperation:didFinishDownload:)]) { dispatch_async(dispatch_get_main_queue(), ^{ // 回到主線程, 傳遞圖片數據給代理對象 [self.delegate downloadOperation:self didFinishDownload:image]; }); } } } @end