iOS多线程开发
一、pthread创建与使用
pthread是纯C语言实现的,属于跨平台的
使用前需要先导入头文件 #import pthread.h
定义一个函数
void *run(void *param)
{
NSString *str = (__bridge NSString *)(param);
// 耗时操作放在这里执行
for (int i = 0; i < 20000; i++) {
NSLog(@"%@----%@", [NSThread currentThread], str);
}
return NULL;
}
使用pthread创建线程
- (void)test
{
// 声明一个线程变量
pthread_t threadId;
/*
参数:
1. 要开的线程的变量
2. 线程的属性
3. 要在这个子线程执行的函数(任务)
4. 这个函数(任务)需要传递的参数
*/
id str = @"hello";
pthread_create(&threadId, NULL, run, (__bridge void *)(str));
}
二、NSThread线程
NSThread的三种创建方式
1.隐式创建的方法
[self performSelectorInBackground:@selector(run:) withObject:@"cy"];
2.静态创建的方法
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"hello"];
3.实例化创建的方法
// 实例化一个线程对像
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
// 让线程开始工作,启动线程, 在新开的线程执行run方法
[thread start];
//耗时操作
- (void)run:(NSString *)str
{
for (int i = 0; i < 10; i++) {
NSLog(@"%@--%d", [NSThread currentThread], i);
}
}
线程的属性设置
NSThread *threadA = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"hello"];
threadA.name = @"thraed A"; // 为线程设置名称
threadA.threadPriority = 0.1; // 设置优先级
[threadA start];
NSThread *threadB = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"hello"];
threadB.name = @"thraed B"; // 为线程设置名称
threadB.threadPriority = 1.0; // 设置优先级
[threadB start];
**注意:线程优先级,必须调用很多次的时候,才能体现出来。取值范围:(0.0~1.0),默认值:0.5
优先级的值越大,就越优先被调用
线程的状态
新建(线程被创建)-->就绪(线程调用start方法)-->运行(任务的执行)-->阻塞(等待、或者睡眠,会返回运行状态从新执行)-->消亡(线程耗尽,或者被强制退出调用exit方法)
// 创建一个线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
// 放到可调度线程池,等待被调度。 这时候是就绪状态
[thread start];
- (void)run
{
NSLog(@"%s", __func__);
// 刚进来就睡会, 睡2秒
// [NSThread sleepForTimeInterval:2.0];
// 睡到指定的时间点
// [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];
for (int i = 0; i < 20; i++) {
// 满足某一个条件以后,阻塞线程的执行。 也就是让线程休息一会
if (i == 10) {
[NSThread sleepForTimeInterval:3.0];
}
// 一旦达到某一个条件,就强制终止线程的执行
if (i == 15) {
// 一旦强制终止,就在不能重新启动
// 一旦强制终止,后面的代码都不会执行
[NSThread exit];
}
NSLog(@"%@--- %d", [NSThread currentThread], i);
}
NSLog(@"线程结束");
}
线程安全隐患
- 加锁,互斥锁
- 加锁,锁定的代码尽量少。
- 加锁范围内的代码, 同一时间只允许一个线程执行
- 互斥锁的参数:任何继承 NSObject *对象都可以。
- 要保证这个锁,所有的线程都能访问到, 而且是所有线程访问的是同一个锁对象
演示代码
@property(nonatomic, assign) int tickets;
// 共享的资源
self.tickets = 20;
// 线程A
NSThread *threadA = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicktes) object:nil];
threadA.name = @"售票员A";
[threadA start];
// 线程B
NSThread *threadB = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicktes) object:nil];
threadB.name = @"售票员B";
[threadB start];
- (void)saleTicktes
{
while (YES) {
// 1.模拟一下延时,卖一张休息1秒
[NSThread sleepForTimeInterval:1.0];
// 加锁的位置可以传任意对象
NSObject *obj = [[NSObject alloc] init];
// 2.加锁,性能差不推荐,self指的是当前类本身
`@synchronized(self)`{ //可任意对象
//3. 判断是否还有票,
if (self.tickets > 0) {
// 4. 如果有票,就卖一张
self.tickets--;
NSLog(@"剩余的票数--%d--%@", self.tickets,[NSThread currentThread]);
}else {
//5.如果没有就返回
NSLog(@"没票了");
break;
}
}
}
}
在主线程更新UI有什么好处?
a.只在主线程更新UI,就不会出现多个线程同时改变 同一个UI控件</br>
b.主线程的优先级最高,也就意味UI的更新优先级高,会让用户感觉很流畅
线程安全的概念
就是在多个线程同时执行的时候,能够保证资源信息的准确性.
atomic原子属性--默认属性
- 原子属性就是针对多线程设计的。 原子属性实现 单(线程)写 多(线程)读
- 因为写的安全级别要求更高。 读的要求低一些,可以多读几次来保证数据的正确性</br>
- 如果同时重写了setter和getter方法,"_成员变量" 就不会提供</br>
- 可以使用 @synthesize 合成指令,告诉编译器属性的成员变量的名称
自旋锁和互斥锁
- 共同点: 都可以锁定一段代码。 同一时间, 只有线程能够执行这段锁定的代码
- 区别:互斥锁,在锁定的时候,其他线程会睡眠,等待条件满足,再唤醒自旋锁,在锁定的时候, 其他的线程会做死循环,一直等待这条件满足,满足后立马去执行,少了一个唤醒过程
三、GCD开发
1.主线程间的通信
开启子线程调用下载图片方法
[self performSelectorInBackground:@selector(downloadImage) withObject:nil];
由子线程反回到主线程的三种方式
1.方式一
[self performSelectorOnMainThread:@selector(downloadFinish:) withObject:image waitUntilDone:NO];
2.方式二
[self performSelector:@selector(downloadFinish:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
3.方式三
[self.iconView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
开启下载任务
//1.url, 确定一个网络上的资源路径
NSURL *url = [NSURL URLWithString:@"http://h.hiphotos.baidu.com/image/pic/item/5366d0160924ab1828b7c95336fae6cd7b890b34.jpg"];
//2.通过url可以下载对应的网络资源, 网络资源传输的都是二进制
NSData *data = [NSData dataWithContentsOfURL:url];
//3.二进制转成图片
UIImage *image = [UIImage imageWithData:data];
//4.下载完成回到主线程中更新图片
[self performSelectorOnMainThread:@selector(downloadFinish:) withObject:image waitUntilDone:NO];
下载完成回到主线程刷新UI
- (void)downloadFinish:(UIImage *)image
{
NSLog(@"%s---%@", __func__, [NSThread currentThread]);
self.iconView.image = image;
}
总结:子线程里面的runloop默认不开启, 也就意味不会自动创建自动释放池, 子线程里面autorelease的对象 就会没有池子可放。 也就意味后面没办法进行释放。 造成内存泄露
2.GCD的使用
核心概念:
- 任务:block
- 队列:把任务放到队列里面,队列先进先出的原则
- 串行队列:顺序,一个一个执行(必须一个任务执行完了,才能从队列里面取出下一个任务)
- 并发队列:同时,同时执行很多个任务(可以同时取出很多个任务,只要有线程去执行)
- 同步sync:不会开新线程
- 异步async:会开新线程,多线程的代名词
- 串行队列同步执行:不开线程,在原来线程里面一个一个顺序执行
- 串行队列异步执行:开一条线程,在这个新线程里面一个一个顺序执
- 并发队列异步执行:开多个线程,并发执行(不一定是一个一个)执
- 并发队列同步执行:不开线程,在原来线程里面一个一个顺序执行
串行队列-同步执行
- 串行队列:顺序,一个一个执行
- 同步任务:不会开辟新线程,是在当前线程执行
- 结果:不开新线程,在当前线程顺序执行
- dispatch : 调度,GCD里面函数,都是以dispatch开头的
1. 创建一个串行队列
// 参数一:队列标签 参数二:队列的属性
dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
2.同步执行任务
// 一般只要使用”同步“执行,串行队列对添加的同步任务,会立马执行
dispatch_sync(queue, ^{
NSLog(@"%@", [NSThread currentThread]);
});
串行队列-异步执行
- 串行队列:一个一个执行
- 异步执行:肯定会开新线程,在新线程执行
- 结果:只会开一个线程,而且所有任务都在这个新的线程里面执行
1. 串行队列
// 下面两种写法是一样的
//dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue = dispatch_queue_create("itcast", NULL);
// 2. 异步执行
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
并发队列-同步执行
- 并发队列:可以同时执行多个任务
- 同步任务:不会开辟新线程,是在当前线程执行
- 结果:不开新线程,顺序一个一个执行。
//1. 并行队列
dispatch_queue_t queue = dispatch_queue_create("cz", DISPATCH_QUEUE_CONCURRENT);
// 2. 同步执行任务
for (int i = 0; i < 10; i++) {
dispatch_sync(queue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
并发队列-异步执行
- 并发队列:可以同时执行多个任务
- 异步执行:会开新线程,在新线程执行
- 结果:会开很多个线程,同时执行
//1. 并行队列
dispatch_queue_t queue = dispatch_queue_create("cz", DISPATCH_QUEUE_CONCURRENT);
// 2. 异步执行任务
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
主队列-异步执行
- 主队列:专门负责在主线程上调度任务,不会在子线程调度任务,在主队列不允许开新线程.
- 异步执行: 会开新线程,在新线程执行
- 结果: 不开线程, 只能在主线程上面,顺序执行!
// 1. 获得主队列-> 程序启动,--> 至少有一个主线程-> 一开始就会创建主队列
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"1----");
// 2. 异步执行任务
for (int i = 0; i < 10; i++) {
NSLog(@"调度前---");
// 异步:把任务放到主队列里,但是不需要马上执行
dispatch_async(queue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
NSLog(@"睡会");
[NSThread sleepForTimeInterval:2.0];
}
NSLog(@"完成----");
主队列-同步执行
- 主队列:专门负责在主线程上调度任务,不会在子线程调度任务,在主队列不允许开新线程.
- 同步执行:要马上执行
- 结果:死锁
1. 获得主队列-> 程序启动,--> 至少有一个主线程-> 一开始就会创建主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2. 同步执行任务
for (int i = 0; i < 10; i++) {
NSLog(@"调度前---");
// 同步:把任务放到主队列里,但需是马上执行
dispatch_sync(queue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
NSLog(@"睡会");
[NSThread sleepForTimeInterval:2.0];
}
全局队列-同步执行
主队列:如果主队列正在执行代码,就不调度任务<BR>
同步执行:如果第一个任务没有执行,就继续等待第一个任务执行完毕,再执行下一个任务此时相互等待,程序无法往下执行(死锁)代码从上往下执行,相互等待产生死锁
for (int i = 0; i < 10; i++) {
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"hello :%@ %d",[NSThread currentThread],i);
});
}
主队列同步执行(解决死锁的问题)
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 10; i++) {
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"hello :%@ %d",[NSThread currentThread],i);
});
}
})
全局队列-异步执行
第一个参数: 一般写0
第二个参数:保留参数 0
iOS7
DISPATCH_QUEUE_PRIORITY_HIGH 2 高优先级
DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默认优先级
DISPATCH_QUEUE_PRIORITY_LOW (-2) 低优先级
DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后台优先级
(可以适配iOS7 &iOS8)iOS8的质量与iOS7的对应的
QOS_CLASS_DEFAULT 0
// 获得全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 添加异步任务
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
全局队列跟并发队列的区别
- 全局队列没有名称 并发队列有名称
- 全局队列,是供所有的应用程序共享。
- 在MRC开发,并发队列,创建完了,需要释放。 全局队列不需要我们管理
同步任务的作用一
有一个小说网站->必须登录->才能下载小说
// 1.并发队列
dispatch_queue_t queue = dispatch_queue_create("cy", DISPATCH_QUEUE_CONCURRENT);
// 2.同步任务,需要马上执行。 不开新线程
dispatch_sync(queue, ^{
NSLog(@"用户登录 %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"下载小说A %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"下载小说B %@", [NSThread currentThread]);
});
控制台打印结果
2018-03-09 00:23:52.137020+0800 05-GCD的使用[25734:836418] 用户登录 <NSThread: 0x600000075d80>{number = 1, name = main}
2018-03-09 00:23:52.137341+0800 05-GCD的使用[25734:836691] 下载小说B <NSThread: 0x60000046b9c0>{number = 6, name = (null)}
2018-03-09 00:23:52.137375+0800 05-GCD的使用[25734:836692] 下载小说A <NSThread: 0x60400046bf40>{number = 5, name = (null)}
同步任务的作用二
做三件耗时的操作:appStore->输入密码->扣费->下载应用
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 1.全局队列-同步下载
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"输入密码: %@",[NSThread currentThread]);
});
// 2.全局队列-同步下载
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"扣费: %@",[NSThread currentThread]);
});
// 3.全局队列-异步下载
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"下载应用: %@",[NSThread currentThread]);
});
});
控制台打印结果
2018-03-09 00:21:09.547556+0800 05-GCD的使用[25639:833211] 输入密码: <NSThread: 0x60000027d300>{number = 3, name = (null)}
2018-03-09 00:21:09.547794+0800 05-GCD的使用[25639:833211] 扣费: <NSThread: 0x60000027d300>{number = 3, name = (null)}
2018-03-09 00:21:09.548115+0800 05-GCD的使用[25639:833211] 下载应用: <NSThread: 0x60000027d300>{number = 3, name = (null)}
阶段性总结:
- 开不开线程,由执行任务方法决定,同步不开线程,异步肯定开线程</br>
- 开多少线程,由队列决定,串行 最多 开一个线程, 并发可以开多个线程。 具体开多少个,有GCD底层决定,程序猿不能控制
GCD其他用法
一次性执行
// GCD创建的一次执行
static dispatch_once_t onceToken;
NSLog(@"%ld", onceToken);
dispatch_once(&onceToken, ^{
NSLog(@"%----ld", onceToken);
});
NSLog(@"完成");
GCD延时操作一
// 参数: when->表示从现在开始,经过多少纳秒以后
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
dispatch_after(when, dispatch_get_global_queue(0, 0), ^{
NSLog(@"点我了-- %@", [NSThread currentThread]);
});
GCD延时操作二
// GCD创建方式(延时2秒)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
// 隐式创建线程(延时2秒)
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
// 定时器方式(延时2秒)
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
GCD-调度组
// 开发的时候,有的时候出现多个网络请求都完成以后(每一个网络请求的事件长短不一定),再统一通知用户
// 比如:下载小说:三国演义->红楼梦->西游记->水浒传
// 实例化一个调度组
dispatch_group_t group = dispatch_group_create();
// 队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 任务添加到队列queue
dispatch_group_async(group, queue, ^{
NSLog(@"下载小说A---%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"下载小说B---%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"下载小说X---%@", [NSThread currentThread]);
});
// 在调度组完成通知里,可以跨队列通信
dispatch_group_notify(group, queue, ^{
NSLog(@"下载完成,请观看%@", [NSThread currentThread]); // 异步的
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
});
});
// 获得所有调度组里面的异步任务完成的通知
/**
dispatch_group_notify(group, queue, ^{
NSLog(@"下载完成,请观看%@", [NSThread currentThread]); // 异步的
});
*/
GCD-Barrier阻塞用法
dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
解释:在执行到dispatch_barrier_async以前,异步线程是无序执行的,之后是有序执行的
GCD-dispatch_apply
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(subpaths.count, queue, ^(size_t index) {
});
非GCD单例
static id _instance;
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
@synchronized(self) {
if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
}
return _instance;
}
+ (instancetype)sharedInstance
{
@synchronized(self) {
if (_instance == nil) {
_instance = [[self alloc] init];
}
}
return _instance;
}
- (id)copyWithZone:(NSZone *)zone
{
return _instance;
}
GCD-宏定义单例
使用时导入其头文件和实现文件即可,名称修改成自定义
// .h文件
#define CYSingletonH(name) + (instancetype)shared##name;
// .m文件
#define CYSingletonM(name) \
static id _instance; \
\
+ (instancetype)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [super allocWithZone:zone]; \
}); \
return _instance; \
} \
\
+ (instancetype)shared##name \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instance = [[self alloc] init]; \
}); \
return _instance; \
} \
\
- (id)copyWithZone:(NSZone *)zone \
{ \
return _instance; \
}
四、NSOperation开发
NSOperation的作用
配合使用NSOperation和NSOperationQueue也能实现多线程编程
NSOperation和NSOperationQueue实现多线程的具体步骤
- 先将需要执行的操作封装到一个NSOperation对象中
- 然后将NSOperation对象添加到NSOperationQueue中
- 系统会自动将NSOperationQueue中的NSOperation取出来
- 将取出的NSOperation封装的操作放到一条新线程中执行
- NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类
使用NSOperation有三种方式
- NSInvocationOperation
- NSBlockOperation
- 自定义子类继承NSOperation,实现内部相应的方法
使用block创建Operation
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// 在主线程
NSLog(@"下载1------%@", [NSThread currentThread]);
}];
// 添加额外的任务(在子线程执行)
[op addExecutionBlock:^{
NSLog(@"下载2------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下载3------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下载4------%@", [NSThread currentThread]);
}];
[op start];
使用invocation创建Operation
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
[op start];
注意:
- 默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
- 只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作
NSOperationQueue
作用:
- NSOperation可以调用start方法来执行任务,但默认是同步执行的
- 如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
添加操作到NSOperationQueue中
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block;
最大并发数
概念:同时执行的任务数,比如同时开3个线程执行3个任务,并发数就是3
最大并发数的相关方法
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
队列的取消、暂停、恢复
取消队列的所有操作
提示:也可以调用NSOperation的- (void)cancel方法取消单个操作
- (void)cancel;
- (void)cancelAllOperations;
暂停和恢复队列
- (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列
- (BOOL)isSuspended;
操作的监听
可以监听一个操作的执行完毕
- (void (^)(void))completionBlock;
- (void)setCompletionBlock:(void (^)(void))block;
自定义NSOperation
自定义NSOperation的步骤很简单
重写- (void)main方法,在里面实现想执行的任务
重写- (void)main方法的注意点
- 自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
- 经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应
自定义NSOperation
#import "CYOperation.h"
@implementation CYOperation
/**
* 需要执行的任务
*/
- (void)main
{
for (NSInteger i = 0; i<1000; i++) {
NSLog(@"download1 -%zd-- %@", i, [NSThread currentThread]);
}
if (self.isCancelled) return;
for (NSInteger i = 0; i<1000; i++) {
NSLog(@"download2 -%zd-- %@", i, [NSThread currentThread]);
}
if (self.isCancelled) return;
for (NSInteger i = 0; i<1000; i++) {
NSLog(@"download3 -%zd-- %@", i, [NSThread currentThread]);
}
if (self.isCancelled) return;
}
- (void)operationQueue1
{
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 创建操作(任务)
// 创建NSInvocationOperation
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download2) object:nil];
// 创建NSBlockOperation
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download3 --- %@", [NSThread currentThread]);
}];
[op3 addExecutionBlock:^{
NSLog(@"download4 --- %@", [NSThread currentThread]);
}];
[op3 addExecutionBlock:^{
NSLog(@"download5 --- %@", [NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download6 --- %@", [NSThread currentThread]);
}];
// 创建CYOperation
CYOperation *op5 = [[CYOperation alloc] init];
// 添加任务到队列中
[queue addOperation:op1]; // [op1 start]
[queue addOperation:op2]; // [op2 start]
[queue addOperation:op3]; // [op3 start]
[queue addOperation:op4]; // [op4 start]
[queue addOperation:op5]; // [op5 start]
}
- (void)download1
{
NSLog(@"download1 --- %@", [NSThread currentThread]);
}
- (void)download2
{
NSLog(@"download2 --- %@", [NSThread currentThread]);
}
- (void)operationQueue2
{
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 创建操作
// NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
// NSLog(@"download1 --- %@", [NSThread currentThread]);
// }];
// 添加操作到队列中
// [queue addOperation:op1];
[queue addOperationWithBlock:^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download2 --- %@", [NSThread currentThread]);
}];
}
- (void)opetationQueue3
{
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 设置最大并发操作数
// queue.maxConcurrentOperationCount = 2;
queue.maxConcurrentOperationCount = 1; // 就变成了串行队列
// 添加操作
[queue addOperationWithBlock:^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:0.01];
}];
[queue addOperationWithBlock:^{
NSLog(@"download2 --- %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:0.01];
}];
[queue addOperationWithBlock:^{
NSLog(@"download3 --- %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:0.01];
}];
[queue addOperationWithBlock:^{
NSLog(@"download4 --- %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:0.01];
}];
[queue addOperationWithBlock:^{
NSLog(@"download5 --- %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:0.01];
}];
[queue addOperationWithBlock:^{
NSLog(@"download6 --- %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:0.01];
}];
}
NSOperation操作依赖
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download1----%@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download2----%@", [NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download3----%@", [NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"download4----%@", [NSThread currentThread]);
}
}];
NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download5----%@", [NSThread currentThread]);
}];
op5.completionBlock = ^{
NSLog(@"op5执行完毕---%@", [NSThread currentThread]);
};
// 设置依赖
[op3 addDependency:op1];
[op3 addDependency:op2];
[op3 addDependency:op4];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue addOperation:op5];
NSOperation-线程间的通信
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
__block UIImage *image1 = nil;
// 下载图片1
NSBlockOperation *download1 = [NSBlockOperation blockOperationWithBlock:^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
image1 = [UIImage imageWithData:data];
}];
__block UIImage *image2 = nil;
// 下载图片2
NSBlockOperation *download2 = [NSBlockOperation blockOperationWithBlock:^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
image2 = [UIImage imageWithData:data];
}];
// 合成图片
NSBlockOperation *combine = [NSBlockOperation blockOperationWithBlock:^{
// 开启新的图形上下文
UIGraphicsBeginImageContext(CGSizeMake(100, 100));
// 绘制图片
[image1 drawInRect:CGRectMake(0, 0, 50, 100)];
image1 = nil;
[image2 drawInRect:CGRectMake(50, 0, 50, 100)];
image2 = nil;
// 取得上下文中的图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 结束上下文
UIGraphicsEndImageContext();
// 回到主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
[combine addDependency:download1];
[combine addDependency:download2];
[queue addOperation:download1];
[queue addOperation:download2];
[queue addOperation:combine];
}
/**
* 线程之间的通信
*/
- (void)test
{
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
UIImage *image = [UIImage imageWithData:data];
// 回到主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
}
网友评论