先看看三种线程的区别
#pragma mark - NSThread 自己的笔记 线程的基本使用
[NSThread detachNewThreadSelector:@selector(threadMain1:) toTarget:self withObject:@(20)];//detach派遣
NSThread *thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(threadMain2:) object:dict];
[thread start];
//线程结束之后的通知 系统自己发送的通知(先判断是哪个线程的通知) 、、添加观察者,看看哪个线程结束了
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(threadFinish:) name:NSThreadWillExitNotification object:nil];
//在子线程访问主线程
[self performSelectorOnMainThread:@selector(changeProgressView:) withObject:@(i) waitUntilDone:NO];
//线程同步和线程异步有什么区别?(重要基础知识)
//打个比方,如果你在等一个人,
//同步的时候,你会一直等到她来了之后才做其他事情,这个过程除了等待你啥都不会做,
//异步的时候,你一边在等,可能一边玩游戏或者是看报纸什么的,一直到她到来,你的等待状态才会结束
//在实现上,同步的过程会阻塞进程的所有其他操作,将同步转换为异步的最常见方法则是
//将会阻塞进程的等待操作放入到一个新的进程中,同时为该等待操作添加一个监视器,在检测到等待操作完成的时候结束等待的进程。
//线程是进程的组成单位 进程重复使用的代码块
//比如音乐播放器 是一个进程 那么下载音乐 播放音乐就是线程工作
//一个线程同一时刻只能完成一个任务,如果多任务交给一个线程完成,那么这和谐任务排好队按顺序一个一个完成,这种情况我们称他为串行,弊端就是cpu利用率比较低,用户体验差,一般我都把多个任务交给多个线程执行,互不影响,我们称这种情况为并发
//线程分为两种 1.主线程 又被成为UI线程 是由编译器自动生成的不能自动修改,比如控件的创建,动画效果,动画的添加都是在主线程中进行的名字叫MianThread 当主线程在一定时间内重复执行某一个函数就会造成屏幕假死,直到时间结束屏幕次啊能恢复于用户交互的效果.因为UI的线程被占用了 2.工作线程又称次线程是由编程人员自己创建可以让让编程人员任意修改
// NSThread
// NSOperation 对象放在队列中实现多线程操作,
// GCD
//点击按钮分别用类方法和对象方法(手动开启线程)创建线程并打印
- (void)pressBtn:(UIButton *)button
{
//1.创建工作线程
if (button.tag == 1) {
//使用类方法创建线程 类方法创建线程不需要手动开启线程,要是实例方法就要手动开启线程
[NSThread detachNewThreadSelector:@selector(threadMain1:) toTarget:self withObject:@(20)];//detach派遣
}else
{
//2.使用实例方法创建线程
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(threadMain2:) object:@(20)];
//实例方法创建线程必须手动开启线程否知不会调用threadMain2这个方法
//开启线程
[thread start];
//3.隐式创建(自动启动)
[self performSelectorInbackground:@selector(download:) withObject:nil];
}
}
- (void)threadMain1:(NSNumber *)num
{
NSLog(@"xiancheng线程1开始工作");
for (int i = 0; i < num.intValue; i ++) {
NSLog(@"i = %d", i +1);
[NSThread sleepForTimeInterval:0.1];
}
NSThread *thread = [NSThread currentThread];
thread.name = @"线程1";
NSLog(@"线程1结束");
//线程结束会从cpu完全退出,不会继续占用cpu
}
- (void)threadMain2:(NSNumber *)num
{
NSLog(@"线程2开始工作");
NSThread *thread = [NSThread currentThread];
thread.name = @"线程2";
for (int i = 0; i
NSLog(@"i = %d",i +1);
[NSThread sleepForTimeInterval:1];
}
NSLog(@"线程2完成工作");
}
//线程之间的相互影响
#import "ViewController.h"
#import
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self createUI];
//线程结束之后的通知 系统自己发送的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(threadFinish:) name:NSThreadWillExitNotification object:nil];
}
- (void)threadFinish:(NSNotification *)notify
{
NSThread *thread = [NSThread currentThread];
NSLog(@"线程%@结束",thread.name);
//添加线程结束通知音
NSString *path = [[NSBundle mainBundle]pathForResource:@"sound" ofType:@"wav"];
NSURL *url = [NSURL fileURLWithPath:path];
SystemSoundID SID;
AudioServicesCreateSystemSoundID((__bridge CFURLRef)url, &SID);
AudioServicesPlaySystemSound(SID);
}
- (void)createUI
{
UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(100, 100, 100, 100);
[button setTitle:@"线程" forState:UIControlStateNormal];
[button setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[button addTarget:self action:@selector(pressBtn:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:button];
}
- (void)pressBtn:(UIButton *)button
{
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(threadMain1:) object:nil];
thread.name = @"线程1";
[thread start];
NSDictionary *dict = @{@"thread":thread,@"num":@(100)};
NSThread *thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(threadMain2:) object:dict];
thread2.name = @"线程2";
[thread2 start];
}
- (void)threadMain2:(NSDictionary *)dict
{
NSLog(@"%@开始",[[NSThread currentThread] name]);
for (int i =0; i <[dict[@"num"] integerValue]; i ++) {
NSLog(@"线程2 = %d",i +1);
[NSThread sleepForTimeInterval:0.1];
}
NSLog(@"线程2结束");
//线程2结束让线程1立即停止
[dict[@"thread"] cancel];
}
- (void)threadMain1:(id)num
{
int i = 0;
while (1) {
NSLog(@"线程1 = %d",i ++);
[NSThread sleepForTimeInterval:1];
//判断是否能接收到cancel消息接收到消息就从cpu中消失
if ([[NSThread currentThread] isCancelled]) {
NSLog(@"线程1接收到cancel消息");
[NSThread exit];
}
}
}
//线程锁 对.m进行操作的代码段进行加锁
#import "ViewController.h"
@interface ViewController ()
{
NSLock *threadLock;
int m;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self createUI];
threadLock = [[NSLock alloc]init];
m = 0;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(threadFinish:) name:NSThreadWillExitNotification object:nil];
}
- (void)threadFinish:(NSNotification *)notify
{
NSThread *thread = [NSThread currentThread];
NSLog(@"线程%@结束",thread.name);
NSLog(@"m = %d",m);
}
- (void)createUI
{
UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(100 , 100, 100, 30);
[button setTitle:@"线程" forState:UIControlStateNormal];
[button setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[button addTarget:self action:@selector(pressBtn:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
- (void)pressBtn:(UIButton *)button
{
NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(threadMain1:) object:nil];
thread1.name = @"线程1";
[thread1 start];
NSThread *thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(threadMain2:) object:nil];
thread2.name = @"线程2";
[thread2 start];
}
- (void)threadMain1:(id)object
{
[threadLock lock];
for (int i = 0; i <10; i ++) {
m ++;
[NSThread sleepForTimeInterval:0.1];
NSLog(@"m = %d",m);
}
[threadLock unlock];
NSLog(@"线程1结束了");
}
- (void)threadMain2:(id)object
{
[threadLock lock];
for (int i = 0; i<10; i ++) {
m--;
[NSThread sleepForTimeInterval:0.1];
NSLog(@"m = %d",m);
}
[threadLock unlock];
NSLog(@"线程2结束");//最好把线程结束写在通知里面
}
#pragma mark -
#import "XLViewController.h"
@interface XLViewController ()
{
UIProgressView * progressView;
}
@end
@implementation XLViewController
-(void)createUIButton
{
UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame = CGRectMake(100, 100, 100, 100);
[btn setTitle:@"线程" forState:UIControlStateNormal];
[btn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[btn addTarget:self action:@selector(pressBtn:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
-(void)pressBtn:(id)sender
{
//创建工作线程 防止主线程出现假死的情况
[NSThread detachNewThreadSelector:@selector(threadMain:) toTarget:self withObject:@(100)];
}
-(void)threadMain:(NSNumber *)num
{
for(int i = 0;i<=num.intValue;i++)
{
// progressView.progress = i / num.floatValue;
// NSLog(@"%f",progressView.progress);
//【注意】主线程不能手动修改 必须借助系统函数
[self performSelectorOnMainThread:@selector(changeProgressView:) withObject:@(i) waitUntilDone:NO];
[NSThread sleepForTimeInterval:0.1];
}
}
-(void)changeProgressView:(NSNumber *)num
{
progressView.progress = num.floatValue / 100;
NSLog(@"%f",progressView.progress);
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self createUIButton];
progressView = [[UIProgressView alloc]initWithFrame:CGRectMake(10, 50, self.view.frame.size.width - 20, 10)];
progressView.progress = 0;
[self.view addSubview:progressView];
}
@end
//- 小球的重力动画效果,从而得知工作线程的作用 (主线程有动画,工作线程还在打印互不影响)
//小球的动画效果在主线程中,打印在工作线程中所以互不影响
(并发和串行是一个线程之内的问题 同步和异步多个线程之间的问题)
#pragma mark - GCD 自己的笔记
// dispatch_sync : 同步,不具备开启线程的能力
// dispatch_async : 异步,具备开启线程的能力
//同步就是指一个线程要等待上一个线程执行完之后才开始执行当前的线程异步是指一个线程去执行,它的下一个线程不必等待它执行完就开始执行
// 并发队列 :多个任务可以同时执行,当然是每个线程执行一个任务
// 串行队列 :一个任务执行完后,再执行下一个任务
// Foundation : OC
// Core Foundation : C语言
// Foundation和Core Foundation框架的数据类型可以互相转换的
//NSString *str = @"123"; // Foundation
//CFStringRef str2 = (__bridge CFStringRef)str; // Core Foundation
//NSString *str3 = (__bridge NSString *)str2;
// CFArrayRef ---- NSArray
// CFDictionaryRef ---- NSDictionary
// CFNumberRef ---- NSNumber
// Core Foundation中手动创建的数据类型,都需要手动释放
// CFArrayRef array = CFArrayCreate(NULL, NULL, 10, NULL);
// CFRelease(array);
//
//
// CGPathRef path = CGPathCreateMutable();
// CGPathRetain(path);
//
// CGPathRelease(path);
// CGPathRelease(path);
/**
凡是函数名中带有create\copy\new\retain等字眼, 都应该在不需要使用这个数据的时候进行release
GCD的数据类型在ARC环境下不需要再做release
CF(Core Foundation)的数据类型在ARC\MRC环境下都需要再做release
*/
/**
* async -- 并发队列(最常用)
* 会不会创建线程:会,一般同时开多条
* 任务的执行方式:并发执行
*/
- (void)asyncGlobalQueue
{
// 获得全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 将 任务 添加 全局队列 中去 异步 执行
dispatch_async(queue, ^{
NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片2---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片3---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片4---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片5---%@", [NSThread currentThread]);
});
}
/**
* async -- 串行队列(有时候用)
* 会不会创建线程:会,一般只开1条线程
* 任务的执行方式:串行执行(一个任务执行完毕后再执行下一个任务)
*/
- (void)asyncSerialQueue
{
// 1.创建一个串行队列
dispatch_queue_t queue = dispatch_queue_create("cn.heima.queue", NULL);
// 2.将任务添加到串行队列中 异步 执行
dispatch_async(queue, ^{
NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片2---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片3---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片4---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片5---%@", [NSThread currentThread]);
});
// 3.非ARC,需要释放创建的队列
// dispatch_release(queue);
}
/**
* async -- 主队列(很常用)
*/
- (void)asyncMainQueue
{
// 1.主队列(添加到主队列中的任务,都会自动放到主线程中去执行)
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.添加 任务 到主队列中 异步 执行
dispatch_async(queue, ^{
NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片2---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片3---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片4---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"-----下载图片5---%@", [NSThread currentThread]);
});
}
/**-------------------------------------华丽的分割线-----------------------------------------------------**/
/**
* sync -- 并发队列
* 会不会创建线程:不会
* 任务的执行方式:串行执行(一个任务执行完毕后再执行下一个任务)
* 并发队列失去了并发的功能
*/
- (void)syncGlobalQueue
{
// 获得全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 将 任务 添加到 全局并发队列 中 同步 执行
dispatch_sync(queue, ^{
NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片2---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片3---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片4---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片5---%@", [NSThread currentThread]);
});
}
/**
* sync -- 串行队列
* 会不会创建线程:不会
* 任务的执行方式:串行执行(一个任务执行完毕后再执行下一个任务)
*/
- (void)syncSerialQueue
{
// 创建一个串行队列
dispatch_queue_t queue = dispatch_queue_create("cn.heima.queue", NULL);
// 将 任务 添加到 串行队列 中 同步 执行
dispatch_sync(queue, ^{
NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片2---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片3---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片4---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片5---%@", [NSThread currentThread]);
});
}
/**
* sync -- 主队列(不能用---会卡死)
*/
- (void)syncMainQueue
{
NSLog(@"syncMainQueue----begin--");
// 1.主队列(添加到主队列中的任务,都会自动放到主线程中去执行)
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.添加 任务 到主队列中 异步 执行
dispatch_sync(queue, ^{
NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片2---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"-----下载图片3---%@", [NSThread currentThread]);
});
NSLog(@"syncMainQueue----end--");
}
#pragma mark GCD中线程之间通信举例
//这是GCD中线程之间的通信
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
dispatch_async(HMGlobalQueue, ^{
NSLog(@"donwload---%@", [NSThread currentThread]);
// 1.子线程下载图片 要在子线程 这样比较好
NSURL *url = [NSURL URLWithString:@"http://d.hiphotos.baidu.com/image/pic/item/37d3d539b6003af3290eaf5d362ac65c1038b652.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
// 2.回到主线程设置图片
dispatch_async(HMMainQueue, ^{
NSLog(@"setting---%@ %@", [NSThread currentThread], image);
[self.button setImage:image forState:UIControlStateNormal];
});
});
}
- (void)pressBtn:(UIButton *)button
{
//异步简单理解你在请求一个的时候另一个也可以同时请求,同步就是你要等一个请求完成后,在请求另外一个。
//创建工作线程进行数据请求 不会影响主线程的工作 主线程和工作线程是异步的
//第一个参数是设置队列中线程的级别 默认是平级 第二个参数默认为零
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//线程内部是同步的,线程之间是异步的
NSURL *url = [NSURL URLWithString:PATH];
//将结构下得数据直接转化成NSData是同步请求,外面是异步请求,里面是同步请求,但是总体还是异步请求
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
//使用工作线程来修改主线程的内容
dispatch_async(dispatch_get_main_queue(), ^{
UIImageView *imageView = (UIImageView *)[self.view viewWithTag:100];
imageView.image = image;
});
});
//SDWebImage第三方库的实现就是多线程实现的
}
{
// 队列组
dispatch_group_t group = dispatch_group_create();
// 全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 3秒后自动开启新线程 执行block中的代码
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), queue, ^{
NSLog(@"------task------%@", [NSThread currentThread]);
});
// 一旦定制好延迟任务后,不会卡主当前线程
[self performSelector:@selector(download:) withObject:@"http://555.jpg" afterDelay:3];
// 第一性代码
dispatch_once(&onceToken, ^{
NSLog(@"----once");
HMImageDownloader *downloader = [[HMImageDownloader alloc] init];
[downloader download];
});
// 异步下载
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行耗时的操作
// 回到主线程显示图片
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = fullImage;
});
});
}
#import mark - dispatch_after的使用
dispatch_after能让我们添加进队列的任务延时执行,该函数并不是在指定时间后执行处理,而只是在指定时间追加处理到dispatch_queue (原理)
该方法的第一个参数是time,第二个参数是dispatch_queue,第三个参数是要执行的block。
dispatch_time_t有两种形式的构造方式,第一种相对时间:DISPATCH_TIME_NOW表示现在,NSEC_PER_SEC表示的是秒数,它还提供了NSEC_PER_MSEC表示毫秒。第二种是绝对时间,通过dispatch_walltime函数来获取,dispatch_walltime需要使用一个timespec的结构体来得到dispatch_time_t。
- (void)delay2
{
//1.
// 3秒后回到主线程执行block中的代码
// dispatch_queue_t queue = dispatch_get_main_queue();
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), queue, ^{
// NSLog(@"------task------%@", [NSThread currentThread]);
// });
//2.
3秒后自动开启新线程 执行block中的代码
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), queue, ^{
NSLog(@"------task------%@", [NSThread currentThread]);
});
}
- (void)delay1 (基本不使用)
{
// 一旦定制好延迟任务后,不会卡主当前线程
[self performSelector:@selector(download:) withObject:@"http://555.jpg" afterDelay:3];
}
#pragma mark 一次性代码 (只运行一次的代码,GCD控制的非常好,最好用GCD)
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"----touchesBegan");
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//这里面的代码程序运行的时候无论怎么点击只会执行一次
NSLog(@"----once下载图片");
});
}
#pragma mark 队列组 保证队列里面所有的所有任务处理完再处理notify函数里面的block
/// 1.分别下载2张图片:大图片、LOGO
// 2.合并2张图片
// 3.显示到一个imageView身上
// 2D绘图 Quartz2D
// 合并图片 -- 水印
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 1.队列组
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.下载图片1
__block UIImage *image1 = nil;
dispatch_group_async(group, queue, ^{
NSURL *url1 = [NSURL URLWithString:@"http://g.hiphotos.baidu.com/image/pic/item/f2deb48f8c5494ee460de6182ff5e0fe99257e80.jpg"];
NSData *data1 = [NSData dataWithContentsOfURL:url1];
image1 = [UIImage imageWithData:data1];
});
// 3.下载图片2
__block UIImage *image2 = nil;
dispatch_group_async(group, queue, ^{
NSURL *url2 = [NSURL URLWithString:@"http://su.bdimg.com/static/superplus/img/logo_white_ee663702.png"];
NSData *data2 = [NSData dataWithContentsOfURL:url2];
image2 = [UIImage imageWithData:data2];
});
// 4.合并图片 (保证执行完组里面的所有任务之后,再执行notify函数里面的block)
dispatch_group_notify(group, queue, ^{
// 开启一个位图上下文
UIGraphicsBeginImageContextWithOptions(image1.size, NO, 0.0);
// 绘制第1张图片
CGFloat image1W = image1.size.width;
CGFloat image1H = image1.size.height;
[image1 drawInRect:CGRectMake(0, 0, image1W, image1H)];
// 绘制第2张图片
CGFloat image2W = image2.size.width * 0.5;
CGFloat image2H = image2.size.height * 0.5;
CGFloat image2Y = image1H - image2H;
[image2 drawInRect:CGRectMake(0, image2Y, image2W, image2H)];
// 得到上下文中的图片
UIImage *fullImage = UIGraphicsGetImageFromCurrentImageContext();
// 结束上下文
UIGraphicsEndImageContext();
// 5.回到主线程显示图片
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = fullImage;
});
});
}
- (void)test2 //(稍微不耗时,比较麻烦要判断什么时候图片全部下载完)
{
// 异步下载
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 1.下载第1张
NSURL *url1 = [NSURL URLWithString:@"http://g.hiphotos.baidu.com/image/pic/item/f2deb48f8c5494ee460de6182ff5e0fe99257e80.jpg"];
NSData *data1 = [NSData dataWithContentsOfURL:url1];
self.image1 = [UIImage imageWithData:data1];
[self bindImages];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 2.下载第2张
NSURL *url2 = [NSURL URLWithString:@"http://su.bdimg.com/static/superplus/img/logo_white_ee663702.png"];
NSData *data2 = [NSData dataWithContentsOfURL:url2];
self.image2 = [UIImage imageWithData:data2];
[self bindImages];
});
}
- (void)bindImages
{
if (self.image1 == nil || self.image2 == nil) return;
// 3.合并图片
// 开启一个位图上下文
UIGraphicsBeginImageContextWithOptions(self.image1.size, NO, 0.0);
// 绘制第1张图片
CGFloat image1W = self.image1.size.width;
CGFloat image1H = self.image1.size.height;
[self.image1 drawInRect:CGRectMake(0, 0, image1W, image1H)];
// 绘制第2张图片
CGFloat image2W = self.image2.size.width * 0.5;
CGFloat image2H = self.image2.size.height * 0.5;
CGFloat image2Y = image1H - image2H;
[self.image2 drawInRect:CGRectMake(0, image2Y, image2W, image2H)];
// 得到上下文中的图片
UIImage *fullImage = UIGraphicsGetImageFromCurrentImageContext();
// 结束上下文
UIGraphicsEndImageContext();
// 4.回到主线程显示图片
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = fullImage;
});
}
- (void)test1 //这个比较耗时
{
// 异步下载
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 1.下载第1张
NSURL *url1 = [NSURL URLWithString:@"http://g.hiphotos.baidu.com/image/pic/item/f2deb48f8c5494ee460de6182ff5e0fe99257e80.jpg"];
NSData *data1 = [NSData dataWithContentsOfURL:url1];
UIImage *image1 = [UIImage imageWithData:data1];
// 2.下载第2张
NSURL *url2 = [NSURL URLWithString:@"http://su.bdimg.com/static/superplus/img/logo_white_ee663702.png"];
NSData *data2 = [NSData dataWithContentsOfURL:url2];
UIImage *image2 = [UIImage imageWithData:data2];
// 3.合并图片
// 开启一个位图上下文 (这时候就有一个空图片)
UIGraphicsBeginImageContextWithOptions(image1.size, NO, 0.0);
// 绘制第1张图片
CGFloat image1W = image1.size.width;
CGFloat image1H = image1.size.height;
[image1 drawInRect:CGRectMake(0, 0, image1W, image1H)];
// 绘制第2张图片
CGFloat image2W = image2.size.width * 0.5;
CGFloat image2H = image2.size.height * 0.5;
CGFloat image2Y = image1H - image2H;
[image2 drawInRect:CGRectMake(0, image2Y, image2W, image2H)];
// 从当前上下文得到上下文中的图片
UIImage *fullImage = UIGraphicsGetImageFromCurrentImageContext();
// 结束上下文
UIGraphicsEndImageContext();
// 4.回到主线程显示图片
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = fullImage;
});
});
}
#pragma mark - NSOperation 有一个线程队列
//NSInvocationOperation
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 创建操作
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil];
// operation直接调用start,如果不放到队列中就是是同步执行(在当前线程执行操作)
// [operation start];
// 添加操作到队列中,会自动异步执行(会自动开线程执行)
[queue addOperation:operation];
//NSBlockOperation
//所有的operation都是并发执行的,operation的任务是并行执行的
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"---下载图片----11---%@", [NSThread currentThread]);
}];
//这个操作有两个任务,其他操作有一个任务
[operation1 addExecutionBlock:^{
NSLog(@"---下载图片----12---%@", [NSThread currentThread]);
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"---下载图片----2---%@", [NSThread currentThread]);
}];
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"---下载图片----3---%@", [NSThread currentThread]);
}];
NSBlockOperation *operation4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"---下载图片----4---%@", [NSThread currentThread]);
}];
// 1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 这个就是GCD里面的主队列,主队列里面的东西都会放到主线程执行
// NSOperationQueue *queue = [NSOperationQueue mainQueue];
//[queue setMaxConcurrentOperationCount:3]; 设置最大线程数
// 2.添加操作到队列中(自动异步执行 ) //很像GCD的全局并发队列
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
[queue addOperation:operation4];
}
- (void)test
{
// NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
// NSLog(@"---下载图片----1---%@", [NSThread currentThread]);
// }];
NSBlockOperation *operation = [[NSBlockOperation alloc] init];
[operation addExecutionBlock:^{
NSLog(@"---下载图片----1---%@", [NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"---下载图片----2---%@", [NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"---下载图片----3---%@", [NSThread currentThread]);
}];
[operation start]; //操作会在主线程执行
// 任务数量 > 1,才会开始异步执行
}
#import "ViewController.h"
@interface ViewController ()
{
NSOperationQueue *queue;//创建线程队列
UIProgressView *progress;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self createUI];
//NSPoeration进行多线程任务是将多个线程放到队列中,这种做法能够减少服务器的荷载量,服务器将请求的线程放到队列中,从中随机抽取一定量的线程将请求的数据进行反馈,其余线程在队列继续等待
//NSOperation 进行多线程任务 有自己创建线程的子类
//1.创建队列对象
queue = [[NSOperationQueue alloc]init];
//2.设置队列得最大的工作线程的个数
[queue setMaxConcurrentOperationCount:3];
//3.向队列中添加工作线程
//注意:工作线程的创建方法有两种
//Invocation调取行使
//(1.alloc init)
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(operationMain:) object:@(100)];
//自带结束的方法,不用通知
[operation1 setCompletionBlock:^{
NSLog(@"线程1结束");
}];
[queue addOperation:operation1];
//(2.block)
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i <100; i ++) {
NSLog(@"线程2 时间%@",[NSDate date]);
[NSThread sleepForTimeInterval:0.1];
}
}];
//自带结束的方法
[operation2 setCompletionBlock:^{
NSLog(@"线程2结束");
}];
//放在队列中的线程是随机执行的,顺序不一定
[queue addOperation:operation2];
}
- (void)operationMain:(NSNumber *)num
{
for (int i = 0; i
NSLog(@"线程1 = %d",i+1);
[NSThread sleepForTimeInterval:0.1];
}
}
- (void)pressBtn:(UIButton *)button
{
//再创建一个工作线程 防止主线程出现假死
NSInvocationOperation *operation3 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(operationMain2:) object:@(100)];
[operation3 setCompletionBlock:^{
NSLog(@"线程3结束");
}];
[queue addOperation:operation3];
}
- (void)operationMain2:(NSNumber *)num
{
for (int i = 0; i
//修改主线程 借助系统方法
[self performSelectorOnMainThread:@selector(changeProgress:) withObject:@(i) waitUntilDone:NO];
[NSThread sleepForTimeInterval:0.1];
}
}
- (void)changeProgress:(NSNumber *)num
{
progress.progress = num.floatValue / 100;
NSLog(@"%f",progress.progress);
}
//其他笔记
{
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 主队列
NSOperationQueue *queue = [NSOperationQueue mainQueue];
// 添加操作到队列中(自动异步执行任务,并发)
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载图片1---%@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
// 异步下载图片
// 回到主线程设置图片
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
// 设置依赖 等执行完A在执行B
[operationB addDependency:operationA];
[operationC addDependency:operationB];
// 设置最大并发(最多同时并发执行3个任务)
queue.maxConcurrentOperationCount = 3;
// 添加操作到队列中(自动异步执行任务)
[queue addOperation:operationC];
[queue addOperation:operationA];
[queue addOperation:operationB];
// 添加操作
[queue addOperationWithBlock:^{
NSLog(@"下载图片6---%@", [NSThread currentThread]);
}];
// 取消队列中的所有任务(不可恢复)
[queue cancelAllOperations];
[queue setSuspended:YES]; // 暂停队列中的所有任务
[queue setSuspended:NO]; // 恢复队列中的所有任务
}
#pragma mark 最后:回到主线程的三个方法
1.[self performSelectorOnMainThread:<#(SEL)#> withObject:<#(id)#> waitUntilDone:<#(BOOL)#>];
2. dispatch_async(dispatch_get_main_queue(), ^{
});
3.[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
#pragma mark 关于三种线程的总结
封装成都 是NSThread GCD NSOperation
常用的有三种: NSThread NSOperationQueue GCD。
1、NSThread 是这三种范式里面相对轻量级的,但也是使用起来最负责的,
你需要自己管理thread的生命周期,线程之间的同步。线程共享同一应用程序的部分内存空间,
它们拥有对数据相同的访问权限。你得协调多个线程对同一数据的访问,
一般做法是在访问之前加锁,这会导致一定的性能开销。
2、NSOperationQueue 以面向对象的方式封装了用户需要执行的操作,
我们只要聚焦于我们需要做的事情,而不必太操心线程的管理,同步等事情,
因为NSOperation已经为我们封装了这些事情。
NSOperation 是一个抽象基类,我们必须使用它的子类。
3、 GCD: iOS4 才开始支持,它提供了一些新的特性,以及运行库来支持多核并行编程,
它的关注点更高:如何在多个cpu上提升效率。
总结:
- NSThread是早期的多线程解决方案,实际上是把C语言的PThread线程管理代码封装成OC代码。
- GCD是取代NSThread的多线程技术,C语法+block。功能强大。
- NSOperationQueue是把GCD封装为OC语法,额外比GCD增加了几项新功能。
* 最大线程并发数
* 取消队列中的任务
* 暂停队列中的任务
* 可以调整队列中的任务执行顺序,通过优先级
* 线程依赖
* NSOperationQueue支持KVO。 这就意味着你可以观察任务的状态属性。
但是NSOperationQueue的执行效率没有GCD高,所以一半情况下,我们使用GCD来完成多线程操作。