iOS开发之多线程NSThread,NSOperation,GC
一、多线程NSThread
- 1.多线程基础知识
- 线程与进程的关系
(1). 线程是CPU执行任务的基本单位,一个进程能有多个线程,但同时只能执行一个任务
(2). 进程就是运行中的软件,是动态的
(3). 一个操作系统可以对应多个进程,一个进程可以有多条线程,但至少有一个线程
(4). 同一个进程内的线程共享进程里的资源
(5). 进程不能执行任务
- 主线程
(1). 进程一启动就自动创建
(2). 显示和刷新UI界面
(3). 处理UI事件 - 子线程
(1). 处理耗时的操作
(2). 子线程不能用来刷新UI - 2.NSThread开辟线程的两种方式
(1)创建手动开启方式
第三个参数:就是方法选择器选择方法的参数
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(thread1:) object:@"thread1"];
开启线程
[thread start];
(2)创建并自动开启方式
[NSThreaddetachNewThreadSelector:@selector(threadAction:) toTarget:selfwithObject:@"thread3"];
常用属性:
NSThread isMainThread 判断当前线程是否是主线程
NSThread isMultiThreaded 判断当前线程是否是多线程
[NSThread setThreadPriority:1.0]; 设置线程的优先级(0-1)
[NSThread sleepForTimeInterval:2]; 让线程休眠
-
3、一个示例,使用多线程加载一张图片
步骤:
(1)创建一个UIImageView,并放在父视图上
(2)创建一个子线程
(3)通过url获取网络图片
(4)回到主线程
(5)在主线程更新UI首先宏定义图片网址:#define kUrl @"http://store.storeimages.cdn-apple.com/8748/as-images.apple.com/is/image/AppleInc/aos/published/images/s/38/s38ga/rdgd/s38ga-rdgd-sel-201601?wid=848&hei=848&fmt=jpeg&qlt=80&op_sharpen=0&resMode=bicub&op_usm=0.5,0.5,0,0&iccEmbed=0&layer=comp&.v=1454777389943" 定义全局变量:UIImageView *myView;
在这使用第二种方式自动开启线程
[NSThread detachNewThreadSelector:@selector(threadAction:) toTarget:selfwithObject:@"thread3"];
初始化myView
myView = [[UIImageView alloc]initWithFrame:self.view.frame];
[self.viewaddSubview:myView];
- (void)threadAction:(NSString*)sender{
// 从网络加载图片将它转化为data类型的数据
NSData*data = [NSDatadataWithContentsOfURL:[NSURLURLWithString:kUrl]];
UIImage *image = [UIImage imageWithData:data];
// waitUntilDone 设置为YES 意味着UI更新完才会去做其他操作回到主线程
[selfperformSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];
}
- ( void)updateUI:(UIImage*)kimage{
myView.image= kimage;
NSLog(@"updateUI这个方法是所在的线程 %@",[NSThread currentThread]);
}
二、多线程NSOperation 与 NSOperationQueue 搭配进行多线程开发
1.NSInvocationOperation 和 NSOperationQueue 搭配进行多线程开发
-
步骤:
1.创建视图
2.创建线程
3.创建线程队列
4.把线程放在线程队列中
5.在子线程中加载网络资源
6.回到主线程
7.在主线程更新UI -
一个代码示例:
前期工作:宏定义
和全局变量#define kurl @"http://store.storeimages.cdn-apple.com/8748/as-images.apple.com/is/image/AppleInc/aos/published/images/s/38/s38ga/rdgd/s38ga-rdgd-sel-201601?wid=848&hei=848&fmt=jpeg&qlt=80&op_sharpen=0&resMode=bicub&op_usm=0.5,0.5,0,0&iccEmbed=0&layer=comp&.v=1454777389943" @interface ViewController () { UIImageView*myImageView; UIImage*iamges; } @end
1.创建视图
myImageView = [[UIImageView alloc]initWithFrame:CGRectMake(50, 50, 200, 200)];
myImageView.backgroundColor = [UIColor cyanColor];
[self.viewaddSubview:myImageView];2、创建线程
NSInvocationOperation*invoOp = [[NSInvocationOperationalloc]initWithTarget:selfselector:@selector(loadResource) object:nil];
3、创建线程队列
NSOperationQueue*operaTionQu = [NSOperationQueuenew];
4、把线程操作放在线程队列中
[operaTionQu addOperation:invoOp];
5、在子线程中加载网络资源
- (void)loadResource{
NSData*data = [NSDatadataWithContentsOfURL:[NSURLURLWithString:kurl]];
iamges= [UIImageimageWithData:data];
6、回到主线程
[[NSOperationQueuemainQueue] addOperationWithBlock:^{
7、更新UI
myImageView.image = iamges;
}];
}
2.NSBlockOperation 和 NSOperationQueue搭配
一个代码示例:
前期工作:宏定义和全局变量
#define kurl @"http://store.storeimages.cdn-apple.com/8748/as-images.apple.com/is/image/AppleInc/aos/published/images/s/38/s38ga/rdgd/s38ga-rdgd-sel-201601?wid=848&hei=848&fmt=jpeg&qlt=80&op_sharpen=0&resMode=bicub&op_usm=0.5,0.5,0,0&iccEmbed=0&layer=comp&.v=1454777389943"
@interface TwoViewController ()
{
UIImageView *myImageView;
UIImage *images;
}
@end
1、创建视图
myImageView = [[UIImageView alloc]initWithFrame:CGRectMake(50, 50, 200, 200)];
myImageView.backgroundColor = [UIColor cyanColor];
[self.viewaddSubview:myImageView];
2、创建一个线程
NSBlockOperation*blockOperation = [NSBlockOperationblockOperationWithBlock:^{
5、在子线程中加载网络资源
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:kurl]];
images = [UIImage imageWithData:data];
6、回到主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
7、 更新UI
myImageView.image = images;
}];
}];
3、创建线程队列
NSOperationQueue*opQu = [NSOperationQueuenew];
作放进线程队列里
[opQu addOperation:blockOperation];
3.用自定义NSOperation的类与NSOperationQueue搭配
代码示例:
新建类CustomOperation继承于NSOperation
并导入框架#import <UIKit/UIKit.h>
CustomOperation.h文件
@property (nonatomic,retain) UIImageView *iamgeViews;
- (instancetype)initWith:(UIImageView*)imageView;
CustomOperation.m文件
宏定义:#define kurl @"http://store.storeimages.cdn-apple.com/8748/as-images.apple.com/is/image/AppleInc/aos/published/images/s/38/s38ga/rdgd/s38ga-rdgd-sel-201601?wid=848&hei=848&fmt=jpeg&qlt=80&op_sharpen=0&resMode=bicub&op_usm=0.5,0.5,0,0&iccEmbed=0&layer=comp&.v=1454777389943"
- (instancetype)initWith:(UIImageView *)imageView{
self= [super init];
if(self) {
self.iamgeViews = imageView;
}
returnself;
}
- (void)main{
// 需要创建一个自动释放池,因为在这里无法访问到主线程的自动释放池
@autoreleasepool{
// 5、在子线程中加载网络资源
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:kurl]];
UIImage *image = [UIImage imageWithData:data];
// 6、回到主线程
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
// 7、更新UI
self.iamgeViews.image = image;
}];
}
}
在控制器里
1、创建视图
myImageView= [[UIImageViewalloc]initWithFrame:CGRectMake(50, 250, 200, 200)];
myImageView.backgroundColor= [UIColorcyanColor];
[self.viewaddSubview:myImageView];
2、创建一个线程操作在类中重写main方法
CustomOperation*cus = [[CustomOperationalloc]initWith:myImageView];
3、创建一个线程队列
NSOperationQueue*opera = [NSOperationQueuenew];
4、把线程操作放到线程队列中
[opera addOperation:cus];
三、多线程之GCD(推荐)
1.GCD基础知识
- 1、什么是GCD
全称是Grand Central Dispath 纯C语言编写,提供非常多且强大的函数,是目前推荐的多线程开发方法,NSOperation便是基于GCD的封装 - 2、GCD的优势
1.为多核的并行运算提出了解决方案
2.GCD会自动利用更多的CPU内核,比如 双核,四核
3、GCD自动管理线程的生命周期(创建线程,调度任务,销毁线程)
4.程序员只需告诉GCD想要执行什么任务,不需要编写任何线程管理代 - 3、GCD中的两个核心概念
1.任务:执行什么操作
2.队列:用来存放任务 - 4、队列可分为两大类型
(1)串行队列(Serial Dispatch Queue): 只能有一个线程,加入到队列中的操作按添加顺序依次执行,一个任务执行完毕后 才能执行下一个任务
(2)并发队列(Concurrent Dispatch Queue): 可以有多个线程,操作进来之后他会将这些线程安排在可用的处理器上,同时保证先进来的任务优先处理
(3)还有一个特殊的队列就是主队列,主队列中永远只有一个线程-主线程,用来执行主线程的操作任务 - 5、采用GCD做多线程,可抽象为两步
1、找到队列
2、在队列中用同步或者异步的方式执行任务 - 6.执行队列中任务的两种方式
(1)同步:只能在当前线程执行任务,不具备开启新线程的能力
(2)异步:可以在新的线程中执行任务,具备开启新线程的能力 - 7、GCD创建的线程任务有四种方式
2、 GCD的几种搭配方式(注意总结规律进行记忆)
#pragma mark-----串行同步
dispatch_queue_tserialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{
//
NSLog(@"%@",[NSThread currentThread]);
});
#pragma mark-----串行异步
dispatch_queue_tserialQueue1 = dispatch_queue_create("serialQueue1", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue1, ^{
NSLog(@"%@",[NSThread currentThread]);
});
#pragma mark----并行同步
dispatch_queue_tconcurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(concurrentQueue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
#pragma mark----并行异步
dispatch_queue_tconcurrentQueue1 = dispatch_queue_create("concurrentQueue1", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue1, ^{
NSLog(@"%@",[NSThread currentThread]);
});
3、代码实例:(此链接下是GCD示例的demo)
代码示例:
#define kurl @"http://store.storeimages.cdn-apple.com/8748/as-images.apple.com/is/image/AppleInc/aos/published/images/s/38/s38ga/rdgd/s38ga-rdgd-sel-201601?wid=848&hei=848&fmt=jpeg&qlt=80&op_sharpen=0&resMode=bicub&op_usm=0.5,0.5,0,0&iccEmbed=0&layer=comp&.v=1454777389943"
@interface MoreImageViewViewController ()
{
int imageTag;
UIImageView *myImageView;
dispatch_queue_t concurentQueue;
NSOperationQueue *operationQueues;
}
@end
在viewDidLoad中
- (void)viewDidLoad {
[super viewDidLoad];
imageTag = 100;
self.view.backgroundColor = [UIColor greenColor];
self.edgesForExtendedLayout = UIRectEdgeNone;
[self controlBtn];
/*
1、创建多个视图
2、找到并行队列
3、给这个并行队列指定多个任务
4、在子线程加载网络资源
5、回到主线程
6、更新UI
/
// 1、创建多个视图
for (int i=0; i<3; i++) {
for (int j=0; j<2; j++) {
myImageView = [[UIImageView alloc]initWithFrame:CGRectMake(10+j200, 40+i*200, 190, 190)];
myImageView.backgroundColor = [UIColor orangeColor];
myImageView.tag = imageTag++;
[self.view addSubview:myImageView];
}
}
// 2、找到并行队列
// 使用下面这个方式不按顺序 因为下面这句找的是 系统的全局并行队列
// concurentQueue = dispatch_get_global_queue(0, 0);
// 这个方式是按顺序的 用的串行队列
concurentQueue = dispatch_queue_create("concurentQueue", DISPATCH_QUEUE_SERIAL);
// 3、指定任务
for (int index=0; index<6; index++) {
dispatch_async(concurentQueue, ^{
[NSThread sleepForTimeInterval:1];
// 加载网络资源
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:kurl]];
UIImage *image = [UIImage imageWithData:data];
// 5、回到主线程
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_sync(mainQueue, ^{
// 6、刷新UI
for (int i=0; i<6; i++) {
UIImageView *iamgeView = [self.view viewWithTag:100+index];
iamgeView.image = image;
}
});
});
}
}
以下两个方法是暂停和开启线程的
- (void)controlBtn{
UISegmentedControl *segment = [[UISegmentedControl alloc]initWithItems:@[@"暂停",@"开启",]];
segment.frame = CGRectMake(50, 620, 300, 50);
segment.apportionsSegmentWidthsByContent = YES;
[self.view addSubview:segment];
[segment addTarget:self action:@selector(clickSegment:) forControlEvents:UIControlEventValueChanged];
}
- (void)clickSegment:(UISegmentedControl *)sender {
switch (sender.selectedSegmentIndex) {
case 0:{
// 暂停队列
dispatch_suspend(concurentQueue);
}break;
case 1:{
// 恢复队列
dispatch_resume(concurentQueue);
}break;
}
}