复习下线程的基础知识, 这里主要是参考文顶顶多线程篇复习写的。
1、线程间通信示例
从子线程回到主线程
#import "ViewController.h"
@interface ViewController ()
@property (strong, nonatomic) UIImageView *imageView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 20, 300, 300)];
_imageView.backgroundColor = [UIColor redColor];
[self.view addSubview:_imageView];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"donwload---%@", [NSThread currentThread]);
// 1.子线程下载图片 图片地址,其实是一行
NSString *urlStr = @"https://timgsa.baidu.com/timg?image&quality=80"
@"&size=b9999_10000&sec=1559061649110&di=adfd3a30f3bb4868722529859d14ae9d"
@"&imgtype=0&src=http%3A%2F%2Fpic31.nipic.com%2F20130719%2F9885883_095141604000_2.jpg";
NSURL *url = [NSURL URLWithString:urlStr];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
// 2.回到主线程设置图片
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"setting---%@ %@", [NSThread currentThread], image);
self.imageView.image = image;
});
});
}
@end
2、延时执行
iOS常见的延时执行有2种方式
- 调用NSObject的方法
// 2秒后再调用self的run方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
// 取消之前设置self的run方法
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(run) object:nil];
- 使用GCD函数
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒后异步执行这里的代码...
});
3、队列组
有这么1种需求
首先:分别异步执行2个耗时的操作
其次:等2个异步操作都执行完毕后,再回到主线程执行操作
如果想要快速高效地实现上述需求,可以考虑用队列组
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行1个耗时的异步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行1个耗时的异步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步操作都执行完毕后,回到主线程...
});
从网络上下载两张图片,把两张图片合并成一张最终显示在view上。
#import "ViewController.h"
@interface ViewController ()
@property (strong, nonatomic) UIImageView *imageView1;
@property (strong, nonatomic) UIImageView *imageView2;
@property (strong, nonatomic) UIImageView *imageView3;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_imageView1 = [[UIImageView alloc] initWithFrame:CGRectMake(10, 30, 150, 150)];
_imageView2 = [[UIImageView alloc] initWithFrame:CGRectMake(170, 30, 150, 150)];
_imageView3 = [[UIImageView alloc] initWithFrame:CGRectMake(10, 200, 300, 200)];
[self.view addSubview:_imageView1];
[self.view addSubview:_imageView2];
[self.view addSubview:_imageView3];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// 图片1:http://d.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=2b9a12172df5e0fefa1581533d095fcd/cefc1e178a82b9019115de3d738da9773912ef00.jpg
// 图片2:http://h.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=f47fd63ca41ea8d39e2f7c56f6635b2b/1e30e924b899a9018b8d3ab11f950a7b0308f5f9.jpg
//1.创建一个队列组
dispatch_group_t group = dispatch_group_create();
//2.开启一个任务下载图片1
__block UIImage *image1 = nil;
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
image1 = [self imageWithUrl:@"http://d.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/"
@"sign=2b9a12172df5e0fefa1581533d095fcd/cefc1e178a82b9019115de3d738da9773912ef00.jpg"];
NSLog(@"图片1下载完成---%@",[NSThread currentThread]);
});
//3.开启一个任务下载图片2
__block UIImage *image2 = nil;
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
image2 = [self imageWithUrl:@"http://h.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/"
@"sign=f47fd63ca41ea8d39e2f7c56f6635b2b/1e30e924b899a9018b8d3ab11f950a7b0308f5f9.jpg"];
NSLog(@"图片2下载完成---%@",[NSThread currentThread]);
});
//同时执行下载图片1\下载图片2操作
//4.等group中的所有任务都执行完毕, 再回到主线程执行其他操作
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"显示图片---%@",[NSThread currentThread]);
self.imageView1.image = image1;
self.imageView2.image = image2;
//合并两张图片
//注意最后一个参数是浮点数(0.0),不要写成0。
UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 100), NO, 0.0);
[image1 drawInRect:CGRectMake(0, 0, 100, 100)];
[image2 drawInRect:CGRectMake(100, 0, 100, 100)];
self.imageView3.image = UIGraphicsGetImageFromCurrentImageContext();
//关闭上下文
UIGraphicsEndImageContext();
NSLog(@"图片合并完成---%@",[NSThread currentThread]);
});
}
//封装一个方法,传入一个url参数,返回一张网络上下载的图片
- (UIImage *)imageWithUrl:(NSString *)urlStr {
NSURL *url = [NSURL URLWithString:urlStr];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
return image;
}
@end
4、信号量
dispatch_semaphore是GCD用来同步的一种方式,与他相关的共有三个函数
(1)创建一个信号量, value的参数value必须大于或等于0。
dispatch_semaphore_t dispatch_semaphore_create(long value);
(2)使传入的信号量dsema的值加1。
dispatch_semaphore_t dispatch_semaphore_create(long value);
(3)dispatch_semaphore_wait
这个函数会使传入的信号量dsema
的值减1;如果dsema
信号量的值大于0,该函数所处线程就继续执行下面的语句,并且将信号量的值减1;如果desema
的值为0,那么这个函数就阻塞当前线程等待timeout
,如果等待的期间desema
的值被dispatch_semaphore_signal
函数加1了,且该函数即dispatch_semaphore_wait
所处线程获得了信号量,那么就继续向下执行并将信号量减1。如果等待期间没有获取到信号量或者信号量的值一直为0,那么等到timeout
时,其所处线程自动执行其后语句。
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
(4)设置timeout的宏
DISPATCH_TIME_NOW、DISPATCH_TIME_FOREVER
示例代码
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"开始执行");
[NSThread sleepForTimeInterval:2];
NSLog(@"signal");
dispatch_semaphore_signal(semaphore);
});
NSLog(@"等待中");
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"得到信号量了");
5、一次性代码
使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只执行1次的代码(这里面默认是线程安全的)
});
//整个程序运行过程中,只会执行一次。
6、单例模式
- @synchronized方式 加锁的方式比较慢
//.h文件
#import
@interface SingleObject : NSObject
+ (instancetype)shareInstance;
@end
.m文件
#import "SingleObject.h"
@implementation SingleObject
static id _instance;
/**
* alloc方法内部会调用这个方法
*/
+ (id)allocWithZone:(struct _NSZone *)zone {
if (_instance == nil) { // 防止频繁加锁
@synchronized(self) {
if (_instance == nil) { // 防止创建多次
_instance = [super allocWithZone:zone];
}
}
}
return _instance;
}
+ (instancetype)shareInstance {
if (_instance == nil) { // 防止频繁加锁
@synchronized(self) {
if (_instance == nil) { // 防止创建多次
_instance = [[self alloc] init];
}
}
}
return _instance;
}
- (id)copyWithZone:(NSZone *)zone {
return _instance;
}
@end
- diapatch_once方式
//.h文件
#import
@interface SingleObject : NSObject
+ (instancetype)shareInstance;
@end
#import "SingleObject.h"
@implementationSingleObject
// 用来保存唯一的单例对象
static id _instace;
+ (id)allocWithZone:(struct _NSZone *)zone {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instace = [super allocWithZone:zone];
});
return _instace;
}
+ (instancetype)shareInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instace = [[self alloc] init];
});
return _instace;
}
- (id)copyWithZone:(NSZone *)zone {
return _instace;
}
@end