/**
无论使用哪种多线程技术都可以使用
[NSThread currentThread]跟踪查看当前执行所在的线程情况。
num = 1表示在主线程上执行的任务
================================================================
1. NSObject多线程技术
1> 使用performSelectorInBackground可以开启后台线程,执行selector选择器选择的方法
2> 使用performSelectorOnMainThread可以重新回到主线程执行任务,通常用于后台线程更新界面UI时使用
3> [NSThread sleepForTimeInterval:1.0f];
让当前线程休眠,通常在程序开发中,用于模拟耗时操作,以便跟踪不同的并发执行情况!
但是:在程序发布时,千万不要保留此方法!不要把测试中的代码交给客户,否则会造成不好的用户体验。
提示:使用performSelectorInBackground也可以直接修改UI,但是强烈不建议使用。
注意:在使用NSThread或者NSObject的线程方法时,一定要使用自动释放池,否则容易出现内存泄露。
代码演示:
(1)NSObject开辟子线程
[self performSelectorInBackground:@selector(bigDemo)withObject:nil];
(2)用NSThread方法开辟子线程
2.1// 类方法新建一个线程,调用@selector方法
[NSThread detachNewThreadSelector:@selector(bigDemo)toTarget:self withObject:nil];
2.2//成员方法
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(bigDemo)object:nil];
// 启动start线程
[thread start];
被子线程调用的方法:
- (void)bigDemo
{
// 自动释放池
// 负责其他线程上的内存管理,在使用NSThread或者NSObject的线程方法时,一定要使用自动释放池
// 否则容易出现内存泄露。
@autoreleasepool {
NSLog(@"%@", [NSThread currentThread]);
// 模拟网络下载延时,睡眠1秒,通常是在开发中测试使用。
[NSThread sleepForTimeInterval:1.0f];
//强烈不建议直接在后台线程更新界面UI!
// 模拟获取到下载的图像
UIImage *image = [UIImage imageNamed:@"头像1"];
// 在主线程更新图像
// 使用self调用updateImage方法在主线程更新图像
[self performSelectorOnMainThread:@selector(updateImage:)withObject:image waitUntilDone:YES];
// 使用imageView的setImage方法在主线程更新图像
[_imageView performSelectorOnMainThread:@selector(setImage:)withObject:image waitUntilDone:YES];
}
}
被主线程调用的方法:
- (void)updateImage:(UIImage *)image
{
NSLog(@"更新图像-> %@", [NSThread currentThread]);
_imageView.image = image;
}
************************************************************
************************************************************
************************************************************
************************************************************
1、GCD演练是基与C语言的NSOPeration&NSOperationQueue基于OC框架写的
• NSOperation的两个子类NSInvocationOperation、NSBlockOperation
• 工作原理:用NSOperation封装要执⾏行的操作
将创建好的NSOperation对象放NSOperationQueue中
启动OperationQueue开始新的线程执⾏行队列中的操作
1.1、#pragma mark invocation
- (IBAction)operationDemo1
{
NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:selfselector:@selector(opAction)object:nil];
// 如果使用start,会在当前线程启动操作
// [op1 start];
// 1. 一旦将操作添加到操作队列,操作就会启动
[_queue addOperation:op1];
}
#pragma mark - NSOperation演练
- (void)opAction
{
NSLog(@"%@", [NSThread currentThread]);
// 模拟网络加载延时
[NSThread sleepForTimeInterval:1.0f];
// 模拟获取到图像
UIImage *image = [UIImage imageNamed:@"头像1"];
// 设置图像,在主线程队列中设置
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
_imageView.image = image;
}];
}
、、、、、、、、、、、、、、、、、、、
1.2#pragma mark blockOperation
- (IBAction)operationDemo2
{
// 用block的最大好处,可以将一组相关的操作,顺序写在一起,便于调试以及代码编写
[_queue addOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
// 模拟延时
[NSThread sleepForTimeInterval:1.0f];
// 模拟获取到图像
UIImage *image = [UIImage imageNamed:@"头像1"];
// 设置图像,在主线程队列中设置
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
_imageView.image = image;
}];
}];
}
//invocation和blockOperation区别就是一个是用方法调用,一个是代码块调用
1.3、NSOperation对象放NSOperationQueue牛逼之处
#pragma mark 依赖关系,可以使子线程按顺序执行
- (IBAction)operationDemo3:(id)sender
{
_queue=[[NSOperationQueue alloc]init];
// 1. 下载
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载 %@" , [NSThread currentThread]);
}];
// 2. 滤镜
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"滤镜 %@" , [NSThread currentThread]);
}];
// 3. 显示
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"更新UI %@" , [NSThread currentThread]);
}];
// 添加操作之间的依赖关系,所谓“依赖”关系,就是等待前一个任务完成后,后一个任务才能启动
// 依赖关系可以跨线程队列实现
[op2 addDependency:op1];
[op3 addDependency:op2];
// 提示:在指定依赖关系时,注意不要循环依赖,否则不工作。
//[op1 addDependency:op3];
[_queue addOperation:op1];
[_queue addOperation:op2];
[[NSOperationQueue mainQueue] addOperation:op3];
}
1.4、最大并发线程数量,这是NSOperation独有功能
- (IBAction)operationDemo4
{
// 控制同时最大并发的线程数量
[_queue setMaxConcurrentOperationCount:2];
for (NSInteger i =0; i <200; i++) {
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
[_queue addOperation:op];
}
}
****************************************************************************************************************************************************************************************************************************************
********************************************************************************************************************
2、GCD演练是基与C语言的框架写的
- (IBAction)gcdDemo1
{
/**
GCD就是为了在“多核”上使用多线程技术
1> 要使用GCD,所有的方法都是dispatch开头的
2> 名词解释
global 全局
queue 队列
async 异步:执行控制不住先后顺序
sync 同步:主要用来控制方法的被调用的顺序
3> 要执行异步的任务,就在全局队列中执行即可
dispatch_async 异步执行控制不住先后顺序
4> 关于GCD的队列
全局队列 dispatch_get_global_queue
参数:优先级 DISPATCH_QUEUE_PRIORITY_DEFAULT
始终是 0
串行队列
主队列
5> 异步和同步于方法名无关,与运行所在的队列有关!
提示:要熟悉队列于同步、异步的运行节奏,一定需要自己编写代码测试!
同步主要用来控制方法的被调用的顺序
*/
// 1. 队列
dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
// 2. 将任务异步(并发)执行
dispatch_sync(queue, ^{
NSLog(@"a->%@", [NSThread currentThread]);//同步在哪个线程,它执行的方法就在哪个线程
});
dispatch_async(queue, ^{
NSLog(@"b->%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
dispatch_sync(queue, ^{
NSLog(@"我哦我哦我->%@", [NSThread currentThread]);
});
//"我哦我哦我"所在的线程就是这个子线程“不不不不不不不不不”所在的线程
NSLog(@"不不不不不不不不不->%@", [NSThread currentThread]);
});
//从GCD回到主线程的时候必须是异步的
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"main - > %@", [NSThread currentThread]);
});//dispatch_sync方法不能在主队列中调用,因为这会无限期的阻止线程并会导致你的应用死锁。所有通过GCD提交到主队列的任务必须是异步的。
}
2.1#pragma mark 串行队列,里面的操作按顺序执行
create方法,获得的是自己新建的线程,所以create需要我们释放线程(如果是手动管理内存的话)
//dispatch_queue_t用来引导队列变量
//createQueue 就是一个队列变量
//dispatch_queue_create是手动创建队列的方法,两个参数,第一个参数是这个队列的名,第二个参数如果写DISPATCH_QUEUE_SERIAL表示这是一个顺序的队列,这个队列里的方法顺序执行。如果是DISPATCH_QUEUE_CONCURRENT,表示这是一个并行的队列,则这个队列里的方法同时执行
//create出来的默认为default优先级
//用完之后,要释放
//dispatch_release(createQueue);
- (void)makeGCD_Queue
{
//串行队列自行创建,不能get
dispatch_queue_t queue=dispatch_queue_create("Myqueue",DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
for (int i =0 ;i<1000; i++) {
NSLog(@"1");
}
NSLog(@"a->%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"b->%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
for (int i =0 ;i<1000; i++) {
NSLog(@"2");
}
NSLog(@"c->%@", [NSThread currentThread]);
});
//从GCD回到主线程的时候必须是异步的
dispatch_async(queue, ^{
//必须将这个回到主线程的方法,放在这个队列中的一个请求方法,才会按照顺序执行
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"d->%@", [NSThread currentThread]);
});//dispatch_sync方法不能在主队列中调用,因为这会无限期的阻止线程并会导致你的应用死锁。所有通过GCD提交到主队列的任务必须是异步的。
});
}
2.2#pragma mark GCD队列组,并发执行所有任务,所有任务执行完毕后回主队列
- (void)makeGCD_Group {
dispatch_group_t group =dispatch_group_create();
dispatch_queue_t queue =dispatch_get_global_queue(0,0);
dispatch_group_async(group, queue, ^{
for (int i =0 ;i<1000; i++) {
NSLog(@"1");
}
NSLog(@"a->%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@“b->%@“, [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
for (int i =0 ;i<1000; i++) {
NSLog(@"2");
}
NSLog(@“c->%@“, [NSThread currentThread]);
});
//Group队列任务组,并发执行任务组中的任务,全部执行完毕后,再最后用notify告知所有任务完成,并做相应处理
dispatch_group_notify(group,dispatch_get_main_queue(), ^{
NSLog(@“d->%@“, [NSThread currentThread]);
});
}
3、全局队列和并发队列的区别:
1、全局队列没有名字,但是并发队列有名字。有名字可以便于查看系统日志
2、全局队列是所有应用程序共享的。
3、在mrc的时候,全局队列不用手动释放,但是并发队列需要。