系统会自动在子线程中调用传入的函数
{
// 将耗时的操作放在子线程中
/*
第一个参数:pthread_t *restrict 线程的代号
第二个参数:const pthread_attr_t *restrict 线程的属性
第三个参数:void *(*)(void *) 指向函数的指针,将来线程需要执行的方法
第四个参数:void *restrict 给第三个参数的指向函数 传递的参数
*/
pthread_t threadID;
// 只要create一次就会创建一个新的线程
pthread_create(&threadID, NULL, &demo, @"xiao");
}
void *demo(void * index) {
for (int i = 0; i < 100; ++i) {
NSLog(@"%i------%@", i, [NSThread currentThread]);
}
return NULL; // 比较特殊,要有返回值
}
注意点:死了不能再活过来(即要重新调用)
1.几种创建方式
1)alloc-initWithTarget方法
代码:
// 1.创建一个新的线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil];
// 2.启动线程
[thread start];
2)detachNewThreadSelector:方法
代码:
// 分离出一个新的子线程
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];
3)performSelectorInBackground:方法
代码:
// 用后台线程调用run:函数
[self performSelectorInBackground:@selector(run:) withObject:nil];
4)相关属性
2.线程状态
阻塞状态(Blocked):调用了sleep方法/等待同步锁
// 指定时间(阻塞线程,阻塞5秒的时间)
[NSThread sleepForTimeInterval:5.0f];
// 指定日期(阻塞线程,阻塞到现在开始的2秒钟之后)
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
// 指定日期(睡眠醒不来)
[NSThread sleepUntilDate:[NSDate distantFuture]];
[NSThread exit];
死亡状态(Dead):线程任务执行完毕/异常/强制退出(exit)
3.互斥锁(安全隐患)
技巧:如何快速记住加锁的单词
[NSUserDefaults standardUserDefaults] synchronize
快速记忆的方法
// self是锁对象:唯一性
@synchronized:(self) {
// 加锁的内容
}
专业术语:线程同步(多条线程在同一条线上执行)
4.原子和非原子
5.线程间通信
附:下载图片
1.获得下载图片的url
NSURL *url = [NSURL URLWIthString:@"图片下载地址"];
2.下载图片的二进制数据到本地(花费时间最长)
NSData *imageData = [NSData dataWithContentsOfURL:url];
3.把二进制数据转换成image
UIImage *image = [UIImage imageWithData:imageData];
4.回到主线程刷新UI(设置图片)很多个方法
[self performSelector:@selector(showImage:) withObject...];
5.在showImage中设置图片 self.imageView.image = image;
4.同步和异步(能不能开线程)---封装任务,添加任务到队列中
对应的两个函数
同步函数:dispatch_sync
异步函数:dispatch_async
5.队列类型(任务的执行方式)---保持任务,安排|调度任务
6.GCD的基本使用
07 注意同步函数和异步函数在执行顺序上面的差异
7.GCD的常用通信代码模板
1.创建队列: 保存任务,安排|调度任务
// 1.创建队列: 保持任务,安排|调度任务
/*
第一个参数:const char *label 字符串
第二个参数:dispatch_queue_attr_t attr 指示出是并发还是串行
DISPATCH_QUEUE_CONCURRENT 并发
DISPATCH_QUEUE_SERIAL 串行
*/
dispatch_queue_t queue = dispatch_queue_create("xiao", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue = dispatch_queue_create("xiao", DISPATCH_QUEUE_SERIALS);
/*
第一个参数:long identifier 队列的优先级, DISPATCH_QUEUE_PRIORITY_DEFAULT == 0
第二个参数:unsigned long flags 此参数暂时无用,设置为0
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2.创建同步/异步函数: 封装任务,添加任务到队列中
// 2.异步函数: 封装任务,添加任务到队列中
dispatch_async(queue, ^{
// 执行代码
});
// 2.同步函数: 封装任务,添加任务到队列中
dispatch_sync(queue, ^{
// 执行代码
});
3.下载图片小事例
// 1.创建队列(全局并发队列)
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 2.下载图片(耗时)-->放在子线程中(异步函数)
dispatch_async(queue, ^{
// 创建url
NSURL *url = [NSURL URLWithString:@"http://www.qqjia.com/z/06/tu8000_5.jpg"];
// 通过url将图片转换成二进制NSData
NSData *data = [NSData dataWithContentsOfURL:url];
// 将NSData转换成图片
UIImage *image = [UIImage imageWithData:data];
// 更新UI --> 在主线程中
// 如果是通过异步函数调用, 那么会先执行完所有的代码, 再更新UI
// 如果是同步函数调用, 那么会先更新UI, 再执行其它代码
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%@", [NSThread currentThread].name);
self.imageView.image = image;
NSLog(@"先更新UI");
});
NSLog(@"先执行");
});
8.常用函数
1.GCD的延迟执行
// DISPATCH_TIME_NOW:从什么时候开始计时(现在)
// 2.0位置:间隔时间(延迟时间)
// dispatch_get_main_queue():队列,决定block在哪个线程中调用,当是主队列时就是主线程调用
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 代码
});
2.GCD的栅栏函数(barrier
)
// 栅栏函数:可以控制队列中任务的执行顺序,前面的任务执行完毕后执行后面的任务
// 注意:这边只能使用并发队列(但是不能用全局并发队列)
dispatch_barrier_async(queue, ^{
NSLog(@"---------");
};
3.一次性代码函数
// 保证整个程序运行中过程中执行一次,不能放在懒加载中,会为空
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"----once----");
};
4.快速迭代函数
// 第一个参数:size_t iterations 遍历的次数
// 第二个参数:dispatch_queue_t queue 队列,决定block在哪个线程调用,并发队列
// 第三个参数:^(size_t) (需要在size_t后面加上一个参数名)索引
dispatch_apply(size_t iterations, dispatch_queue_t queue, ^(size_t) {
// 代码
});
5.队列组
// 创建队列组
dispatch_group_t group = dispatch_group_create();
// 创建队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 使用队列组的异步函数,添加数组,监听队列里面任务的执行情况
dispatch_group_async(group, queue, ^{
// 代码
}
// 第一种:(拦截通知)当所有任务都执行完毕后,来到该方法
dispatch_group_notify(group, queue, ^{
// 代码
}
// 第二种:(拦截通知)一直等待,等待所有的任务都执行完毕后继续往下执行 | 阻塞
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
小案例:下载两张图片,合成图片
代码
9.使用create函数创建的并发队列和全局并发队列的区别
2.NSOperation基本使用:
相关代码:
1) NSBlockOperation
//1.封装操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
// 主线程执行
NSLog(@"download1---%@",[NSThread currentThread]);
}];
// 追加任务
// 追加的任务在子线程中并发执行
[op1 addExecutionBlock:^{
NSLog(@"download4---%@",[NSThread currentThread]);
}];
//2.开始执行
[op1 start];
2) NSInvocationOpeartion
//1.封装操作
/*
第一个参数:目标对象 self
第二个参数:调用方法
第三个参数:调用方法需要传递的参数
*/
NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil];
//2.启动操作
[op1 start];
3) 自定义继承NSOperation的类
// 自定义一个继承NSOperation的类(重写main函数)
- (void)main
{
NSLog(@"1---%@", [NSThread currentThread]);
}
// 封装操作
LJSubOperation *subOperation1 = [[LJSubOperation alloc] init];
LJSubOperation *subOperation2 = [[LJSubOperation alloc] init];
// 开始执行
[subOperation1 start];
[subOperation2 start];
3.NSOperationQueue基本使用
相关代码:
// 1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.将操作添加到队列中(会自动调用start方法)
[queue addOperation:bOpertion1];
//简便方法:该方法内部会自动将block块里面的任务封装为一个NSBlockOperation对象,然后添加到队列
[queue addOperationWithBlock:^{
// 代码
}];
4.NSOperation的其它用法
4.1 设置最大并发数(控制任务并发和串行)
// 注意点:该属性需要在任务添加到队列中之前进行设置
// 该属性控制队列串行还是并发执行
// 如果该属性设置1,则为串行,大于1则就是并发的
// 系统默认的值位-1,如果该属性设置为0,则不会执行任何任务
queue.maxConcurrentOperationCount = 2;
4.2 暂停.恢复以及取消
// 暂停,只是不执行当前任务下面的操作,但是当前的还是会执行
self.queue.suspended = YES;
// 恢复
self.queue.suspended = NO;
// 取消队列中的所有操作,不可以恢复
[self.queue cancelAllOperations];
// 注意:苹果官方建议,每当执行完一次耗时操作之后,就查看一下当前队列是否为取消状态,如果是,那么就直接退出
// 好处是可以提高程序的性能
if (self.isCancelled) {
return;
}
5.操作依赖和操作监听
// op1和op2都是NSOperation操作,能保证操作1依赖于操作2,执行2再执行1
// 如果互相都依赖对方,那么两个都不依赖
[op1 addDependency:op2];
6.小案例
看附件
看附件
// 1.数据展示
// 2.内存存储(存储图片)NSMutableDictionary *iamges
// 3.沙盒存储(Libriary/caches)
NSString *caches = [NSSearchPathForDirectoriesInDimains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
// 4.问题1:数据混乱(重复下载)-->将操作加到操作缓存中
// 问题2:复用问题:判断之前设置占位图片self.imageView.image = nil;
// 问题3:数据容错处理
// 发生内存警告:1.清空内存缓存 2.关闭所有的队列操作 3.清空所有的下载操作字典
7.附录
原文:http://www.cnblogs.com/LongLJ/p/5084341.html