iOS中实现多线程的技术方案
pthread 实现多线程操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 代码实现: void  * run(void  *param){     for  (NSInteger  i = 0 ; i < 1000 ; i++) {         NSLog (@"---buttonclick---%zd---%@" , i, [NSThread  currentThread]);     }          return  NULL ; } @implementation  ViewController - (IBAction )clickButton:(id )sender {          pthread_t thread;               pthread_create(&thread, NULL , run, NULL );               pthread_t thread2;          pthread_create(&thread2, NULL , run, NULL ); } 
NSThread实现多线程 一个 NSThread 对象就代表一条线程
创建线程的多种方式 
		
1 2 3 4 5 NSThread  *thread = [[NSThread  alloc] initWithTarget:self  selector:@selector (run:) object:@"jack" ];[thread start]; 
1 2 [NSThread  detachNewThreadSelector:@selector (run:) toTarget:self  withObject:@"jack" ]; 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 [self  performSelectorInBackground:@selector (run:) withObject:@"jack" ]; [NSThread  sleepForTimeInterval:2.0 ]; #pragma mark - 执行run方法 - (void )run:(NSString  *)param {          for  (NSInteger  i = 0 ; i < 100 ; i++) {         NSLog (@"---%@---%zd---%d" , [NSThread  currentThread], i,  [NSThread  isMainThread]);     } } 
方法2和方法3的优点:快捷    方法1的优点:可以轻松拿到线程
 
线程间通信 
线程间通信的体现
1个线程传递数据给另1个线程 
在1个线程中执行完特定任务后,转到另1个线程继续执行任务 
 
 
 
线程间通信的常用方法:小程序图片下载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - (void )touchesBegan:(NSSet  *)touches withEvent:(UIEvent  *)event  {          NSURL  *url = [NSURL  URLWithString:@"https://7xjanq.com1.z0.glb.clouddn.com/6478.jpg" ];     NSThread  *thread = [[NSThread  alloc] initWithTarget:self  selector:@selector (downLoadWithURL:) object:url];          [thread start]; } - (void )downLoadWithURL:(NSURL  *)url {     NSLog (@"%@" , [NSThread  currentThread]);          NSData  *data = [NSData  dataWithContentsOfURL:url];          UIImage  *image = [UIImage  imageWithData:data];               [self .imageView performSelectorOnMainThread:@selector (setImage:) withObject:image waitUntilDone:YES ]; } 
以上两种方式使用线程已经过时了,开发中我们操作线程大多都使用 GCD 和 NSOperation 来实现多线程操作。
下面我就给大家系统的介绍一下 GCD 是如何实现多线程的
GCD 实现多线程 GCD 简介 GCD 全称是Grand Central Dispatch,可译为“超级厉害的中枢调度器”,GCD 是苹果公司为多核的并行运算提出的解决方案, GCD会自动利用更多的 CPU 内核(比如双核、四核)来开启线程执行任务,GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程),不需要我们程序员手动管理内存。
任务和队列 任务:在同步函数和异步函数中执行
GCD会自动将队列中的任务取出,放到对应的线程,任务的取出遵循FIFO,即先入先出队列,First Input First Output 的缩写。先进入的任务先完成并结束,再执行后面的任务。
同步函数和异步函数,并发队列和串行队列 
创建并发/串行队列代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 dispatch_queue_t  queueConcurrent = dispatch_queue_create("520it.com" , DISPATCH_QUEUE_CONCURRENT);     dispatch_queue_t  queueSerial = dispatch_queue_create("520it.com" , DISPATCH_QUEUE_SERIAL);     dispatch_queue_t  queueGlobal = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 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 // 后台      dispatch_queue_t  queueMain = dispatch_get_main_queue();
同步/异步函数代码表示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 dispatch_sync (queueSerial, ^{	for  (NSInteger  i = 0 ; i < 10 ; i++) { 		NSLog (@"~~~%@" , [NSThread  currentThread]);    } });      dispatch_sync (queueConcurrent, ^{	for  (NSInteger  i = 0 ; i < 10 ; i++) { 		NSLog (@"~~~%@" , [NSThread  currentThread]); 	} });      dispatch_async (queueSerial, ^{	dispatch_async (queueSerial, ^{ 		for  (NSInteger  i = 0 ; i < 10 ; i++) { 			NSLog (@"~~~%@" , [NSThread  currentThread]); 		} 	}); 	dispatch_async (queueSerial, ^{ 		for  (NSInteger  i = 0 ; i < 10 ; i++) { 			NSLog (@"~~~%@" , [NSThread  currentThread]); 		} 	}); 	dispatch_async (queueSerial, ^{ 		for  (NSInteger  i = 0 ; i < 10 ; i++) { 			NSLog (@"~~~%@" , [NSThread  currentThread]); 		} 	}); });      dispatch_async (queueConcurrent, ^{	dispatch_async (queueSerial, ^{ 			for  (NSInteger  i = 0 ; i < 10 ; i++) { 				NSLog (@"~~~%@" , [NSThread  currentThread]); 			} 	}); 	dispatch_async (queueSerial, ^{ 		for  (NSInteger  i = 0 ; i < 10 ; i++) { 			NSLog (@"~~~%@" , [NSThread  currentThread]); 		} 	}); 	dispatch_async (queueSerial, ^{ 		for  (NSInteger  i = 0 ; i < 10 ; i++) { 			NSLog (@"~~~%@" , [NSThread  currentThread]); 		} 	}); });      
注意
 
解释:使用同步函数添加任务 A 到串行队列,说明要在当前串行队列立即执行任务 A ,任务 A 执行完后,才会执行任务 A 后面的代码。但当前队列是串行队列,也就是说任务 A 必须等到当前串行队列中正在执行的任务 B 完成之后才能执行,因此又必须先执行任务 A 中立即执行任务,又要必须等到任务 B 执行完以后才能执行下一个任务,所以就会卡死。你等我,我等你,谁也无法执行。 
GCD实现线程通信 小项目:下载图片
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 NSURL  *url = [NSURL  URLWithString:@"https://7xjanq.com1.z0.glb.clouddn.com/6478.jpg" ];     dispatch_queue_t  queue = dispatch_queue_create("111" , DISPATCH_QUEUE_CONCURRENT);dispatch_async (queue, ^{	NSData  *data = [NSData  dataWithContentsOfURL:url]; 	UIImage  *image = [UIImage  imageWithData:data]; 	 	 	dispatch_async (dispatch_get_main_queue(), ^{ 		self .imageView.image = image; 	}); }); 
GCD其他常用函数 dispatch_barrier 栅栏 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 dispatch_queue_t  queue = dispatch_queue_create("11" , DISPATCH_QUEUE_CONCURRENT);     dispatch_async (queue, ^{	for  (int  i = 0 ;  i < 100 ; i++) { 		NSLog (@"%@--1" , [NSThread  currentThread]); 	} });      dispatch_async (queue, ^{	for  (int  i = 0 ;  i < 100 ; i++) { 		NSLog (@"%@--2" , [NSThread  currentThread]); 	} });      dispatch_barrier_async(queue, ^{ 	for  (int  i = 0 ;  i < 100 ; i++) { 		NSLog (@"%@--3" , [NSThread  currentThread]); 	} });      dispatch_async (queue, ^{	for  (int  i = 0 ;  i < 100 ; i++) { 		NSLog (@"%@--4" , [NSThread  currentThread]); 	} }); 
dispatch_after 延迟执行 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 [self  performSelector:@selector (run:) withObject:@"参数"  afterDelay:2.0 ]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0  * NSEC_PER_SEC )), dispatch_get_main_queue(), ^{ 	for  (NSInteger  i = 0 ; i < 100 ; i++) { 		NSLog (@"%@" , [NSThread  currentThread]); 	} }); [NSTimer  scheduledTimerWithTimeInterval:2.0  target:self  selector:@selector (run) userInfo:nil  repeats:NO ]; ```	 #### dispatch_once 整个程序运行中执行一次 ```objc static  dispatch_once_t  onceToken;dispatch_once (&onceToken, ^{	 }); 
作用:实现某个类的单粒对象
单例模式:在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次)
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 static  id  _person;+ (instancetype )sharePerson {     static  dispatch_once_t  onceToken;     dispatch_once (&onceToken, ^{         _person = [[super  alloc] init];     });     return  _person; } 	 + (instancetype )allocWithZone:(struct  _NSZone *)zone {     static  dispatch_once_t  onceToken;     dispatch_once (&onceToken, ^{         _person = [super  allocWithZone:zone];     });     return  _person; } 	 - (id )copy  {     return  _person; } 
开发中一般自定义成宏,比较方便,一行代码搞定。
dispatch_apply 快速迭代 示例小程序:将一个文件夹中的图片剪切到另一个文件夹
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 NSString  *from = @"/Users/Ammar/Pictures/壁纸" ;NSString  *to = @"/Users/Ammar/Pictures/to" ;NSFileManager  *manager = [NSFileManager  defaultManager];NSArray  *subPaths = [manager subpathsAtPath:from];dispatch_apply(subPaths.count, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^(size_t index) {    NSLog (@"%@ - %zd" , [NSThread  currentThread], index); 	NSString  *subPath = subPaths[index]; 	NSString  *fromPath = [from stringByAppendingPathComponent:subPath]; 	NSString  *toPath = [to stringByAppendingPathComponent:subPath];          	 	[manager moveItemAtPath:fromPath toPath:toPath error:nil ]; 	NSLog (@"%@---%zd" , [NSThread  currentThread], index); }); 
dispatch_group 队列组 示例小程序:需求下载图片1 下载图片2   将图片1和图片2合成新的图片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 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, ^{ 	NSData  *data = [NSData  dataWithContentsOfURL:[NSURL  URLWithString:@"https://7xjanq.com1.z0.glb.clouddn.com/6478.jpg" ]]; 	self .image1 = [UIImage  imageWithData:data]; 	NSLog (@"1%@" , [NSThread  currentThread]); }); dispatch_group_async(group, queue, ^{ 	NSData  *data = [NSData  dataWithContentsOfURL:[NSURL  URLWithString:@"https://7xjanq.com1.z0.glb.clouddn.com/6478.jpg" ]]; 	self .image2 = [UIImage  imageWithData:data]; 	NSLog (@"2%@" , [NSThread  currentThread]); });      dispatch_group_notify(group, queue, ^{ 	CGFloat  imageW = self .imageView.bounds.size.width; 	CGFloat  imageH = self .imageView.bounds.size.height; 	 	UIGraphicsBeginImageContext (self .imageView.bounds.size);          	 	[self .image1 drawInRect:CGRectMake (0 , 0 , imageW * 0.5 , imageH)]; 	[self .image2 drawInRect:CGRectMake (imageW * 0.5 , 0 , imageW * 0.5 , imageH)];          	 	UIImage  *image = UIGraphicsGetImageFromCurrentImageContext ();     	 	UIGraphicsEndImageContext ();          	 	dispatch_async (dispatch_get_main_queue(), ^{ 		self .imageView.image = image; 	}); 	NSLog (@"3%@" , [NSThread  currentThread]); }); 
GCD定时器 GCD定时器不受Mode影响因此比NSTimer要准确
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 static  int  count = 0 ;dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0  * NSEC_PER_SEC )), dispatch_get_main_queue(), ^{ 	 });      dispatch_queue_t  queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 );          self .timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0 , 0 , queue);     int64_t interval = (int64_t)(2.0  * NSEC_PER_SEC );  dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0  * NSEC_PER_SEC ));       	 dispatch_source_set_timer(self .timer, start, interval, 0 );           dispatch_source_set_event_handler(self .timer, ^{ 	NSLog (@"----------------%@" , [NSThread  currentThread]);          	 	count++; 	if  (count == 5 ) { 		dispatch_cancel(self .timer); 		self .timer = nil ; 	} });      dispatch_resume(self .timer); 
讲完 GCD 就该讲讲 NSOperation,它是 GCD 的面向对象的封装,使用起来也更方便,
NSOperation实现多线程 NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类
NSInvocationOperation 
NSBlockOperation 
自定义子类继承NSOperation,实现内部相应的方法 
 
使用 NSOperation 实现多线程的步骤:
创建任务 NSOperation 对象 
创建 NSOperationQueue 队列 
将任务 NSOperation 对象 add 到 NSOperationQueue 队列中去 
 
NSInvocationOperation 代码如下:
1 2 3 NSInvocationOperation  *op = [[NSInvocationOperation  alloc] initWithTarget:self  selector:@selector (run) object:nil ];[op start]; 
注意:默认情况下,调用了start方法后并不会开一条新的线程去执行,而是在当前线程同步执行操作,只有将 NSOperation 放到一个 NSOperationQueue 中,才会异步执行操作
 
NSBlockOperation 1 2 3 4 5 6 7 8 9 10 11 NSBlockOperation  *op = [NSBlockOperation  blockOperationWithBlock:^{	   	NSLog (@"下载1------%@" , [NSThread  currentThread]); }];      [op addExecutionBlock:^{ 	NSLog (@"下载2------%@" , [NSThread  currentThread]); }]; 自定义Operation:需要实现- (void )main方法,需要做的事情放在mian方法中 
NSOperationQueue 使用NSOperationQueue创建队列:主队列和全局队列
1 2 3 4 5 6 7 8 9 NSOperationQueue  *queue = [[NSOperationQueue  alloc] init];NSOperationQueue  *mainQueue = [NSOperationQueue  mainQueue];queue.maxConcurrentOperationCount = 1 ; 
队列的取消、暂停、恢复: 1 2 3 4 - (void )cancelAllOperations;  - (void )setSuspended:(BOOL )b; 
添加依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 NSOperationQueue  *queue = [[NSOperationQueue  alloc] init];NSBlockOperation  *block1 = [NSBlockOperation  blockOperationWithBlock:^{	NSLog (@"download1 -------------- %@" , [NSThread  currentThread]); }];      NSBlockOperation  *block2 = [NSBlockOperation  blockOperationWithBlock:^{	NSLog (@"download2 -------------- %@" , [NSThread  currentThread]); }];      NSBlockOperation  *block3 = [NSBlockOperation  blockOperationWithBlock:^{	NSLog (@"download3 -------------- %@" , [NSThread  currentThread]); }];           [block3 addDependency:block1]; [block3 addDependency:block2];      [queue addOperation:block1]; [queue addOperation:block2]; [queue addOperation:block3]; 
注意:不能循环依赖,但可以跨队列依赖,不管NSOperation对象在哪个队列。只要是两个NSOperation对象就可以依赖
 
线程间通信 示例:下载图片
1 2 3 4 5 6 7 8 9 10 11 [[[NSOperationQueue  alloc] init] addOperation:[NSBlockOperation  blockOperationWithBlock:^{ 	UIImage  *image = [UIImage  imageWithData:[NSData  dataWithContentsOfURL:[NSURL  URLWithString:@"https://7xjanq.com1.z0.glb.clouddn.com/6478.jpg" ]]];          	 	[[NSOperationQueue  mainQueue] addOperation:[NSBlockOperation  blockOperationWithBlock:^{ 		self .imageView.image = image; 	}]];          }]]; 
示例:下载图片1和图片2 并合成图片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 NSOperationQueue  *queue = [[NSOperationQueue  alloc] init];     __block UIImage  *image1 = nil ; NSBlockOperation  *block1 = [NSBlockOperation  blockOperationWithBlock:^{	image1 = [UIImage  imageWithData:[NSData  dataWithContentsOfURL:[NSURL  URLWithString:@"https://7xjanq.com1.z0.glb.clouddn.com/6478.jpg" ]]]; }];      __block UIImage  *image2 = nil ; NSBlockOperation  *block2 = [NSBlockOperation  blockOperationWithBlock:^{	image2 = [UIImage  imageWithData:[NSData  dataWithContentsOfURL:[NSURL  URLWithString:@"https://7xjanq.com1.z0.glb.clouddn.com/6478.jpg" ]]]; }];      CGFloat  imageW = self .imageView.bounds.size.width;CGFloat  imageH = self .imageView.bounds.size.height;     NSBlockOperation  *block3 = [NSBlockOperation  blockOperationWithBlock:^{	UIGraphicsBeginImageContext (CGSizeMake (imageW, imageH)); 	[image1 drawInRect:CGRectMake (0 , 0 , imageW * 0.5 , imageH)]; 	[image2 drawInRect:CGRectMake (0.5  * imageW, 0 , 0.5  * imageW, imageH)];          	UIImage  *image3 = UIGraphicsGetImageFromCurrentImageContext (); 	UIGraphicsEndImageContext (); 	 	[[NSOperationQueue  mainQueue] addOperation:[NSBlockOperation  blockOperationWithBlock:^{ 		self .imageView.image = image3; 	}]];      }];      [block3 addDependency:block1]; [block3 addDependency:block2];      [queue addOperation:block1]; [queue addOperation:block2]; [queue addOperation:block3]; 
应用 应用:SDWebImage 框架的底层主要功能实现就是基于多线程,使用多线程,我们可以实现小图片的多图片下载。这里的逻辑其实是比较复杂的
实现小图片的多图片下载思路:
代码实现见本文代码。
本文代码见:Multithreading