GCD
-
sizeTOfit:如果让一个label和文字一样大,可以用,如果让一个imageVIew和图片一样大,可以用。
-
无符号整数:%tu NSUintrger 有符号整数:%zd NSInteger。可以适应程序CPU架构。32,64位都不会出错。
-
loadView方法目的就是创建view,如果view等于空,会重复调用这个方法。
(1)GCD:针对多核处理器提供的解决方案。把任务添加到队列,并且执行指定的任务函数,任务都由Block封装(既没参数也没返回值的Block)。是一个并发技术,已经没有线程的感觉了。底层安排是自动实现的,线程池维护的。
(2)同步&异步(任务):
1.同步(sync):前面执行完毕执行下一句。保证原有的代码执行顺序。不管并发还是串行,同步在就是顺序执行。
作用:
2.异步(async):不用等直接执行。异步是多线程的代名词。
(3)GCD原理:任务添加到队列。任务就是Block,队列就是主队列,全局队列啊。然后选择同步异步(sync,async)。GCD会管理线程生命周期。所以看不到线程的感觉。GCD底层会有一个线程池。把任务添加到队列后,队列负责调度执行。队列回去找线程池要可用的线程,然后把任务放到线程,执行完毕后线程空闲,就会回收到线程池。然后如果没人用了,就释放了。像NSThread的detahch和perform,每次添加任务都会增加线程,234567持续。想要做一个线程池不容易。而GCD会自动回收,底层线程池会做一个复用,234555555555.
// 线程间通讯 - (void)gcdDemo3 { dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"玩命下载中...%@", [NSThread currentThread]); // 更新UI dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"更新UI %@", [NSThread currentThread]); }); }); }
5)队列:从数据结构上讲,先进先出。
1.串行队列:不管同步异步,前面不执行完就不调度后面的。只会开启一条线程。
dispatch_queue_t queue = dispatch_queue_create("MY", DISPATCH_QUEUE_SERIAL);
2.并发队列:
dispatch_queue_t queuee = dispatch_queue_create("MY", DISPATCH_QUEUE_CONCURRENT);
总结:开不开线程由函数决定。同步不开异步开。
(异步)开多少线程由队列决定。串行最多一条,并发由底层线程池决定。
线程不是越多越好。IOS7.0使用GCD只能开5-6条。8.0就能开很多。
因为苹果建议大家用NSOperation(OC的),对GCD的封装。
因为GCD还给排列组合。GCD是底层C语言的。
(6)主队列同步&异步:主队列是专门用来在主线程调入任务的。先进先出。必须主线程控线才能调度任务。随着程序启动就创建好了。主线程异步演练结果就是必须等主线程全部完成才执行异步。主队列同步时候会出现互相等的现象,俗称死锁。你等我我等你。(也有不死锁时候)。
(7)主队列同步不死锁:
(8)同步作用:不开线程,不执行完不会调度后续的任务。
- (void)mydemo1 { // 所有耗时操作都是异步 // 同步任务不完成,队列后面的异步也不能执行 dispatch_queue_t queue = dispatch_queue_create("MY", DISPATCH_QUEUE_CONCURRENT); void (^tast)() = ^ { dispatch_sync(queue, ^{ NSLog(@"login%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"download A%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"download B%@",[NSThread currentThread]); }); }; dispatch_async(queue, tast); }
(9)全局队列:
/**
* 全局队列和并发对列的工作特性一致
* ARC 不允许使用 dispatch_release
但是 MRC 就必须要用 dispatch_release(q);
* 全局队列更方便
* 通常开发复杂的网络应用,第三方框架时,才需要使用自定义并发队列!
*/
// 1. 全局队列
// 第1个参数也用0 - 可以保证 iOS 7.0 & 8.0 的适配
// 第2个参数是 0
(10)barrier阻塞:NSMutableArray是线程不安全的,多个线程添加数组有可能会抢,会崩溃。XCODE7自行测试是数组数量不够。10个可能只有七个,还不会崩溃。添加barrier就好了。barrier是把所有异步执行完毕后,在同一个线程顺序执行自己的block。barrier可以卡在异步 barrier 异步中。先执行全部异步,在阻塞统一barrier,在进行下面的异步。而且用的时候必须用自定义的队列。而不是全部队列,全局队列会失效。最主要应用:数据存储IO,下载一堆东西,需要顺序安全输出。
dispatch_barrier_async(_queue, ^{ [self.myarray addObject:image]; NSLog(@"添加完成%@--%@",filename,[NSThread currentThread]); });
(11)一次性执行:dispatch_once (单例)如果要保证对象只被实例化一次,就要放到静态区。因为block内部想要修改外部的变量,第一反应是用__block,但是当onceToken = 0时候还可以,当单例执行了一次后,出了代码块就消失了,盛夏时候onceToken != 0就无法,此时对象永远都为nil。所以用static来修饰。并且dispatch_once是同步的。是线程安全的。内部也有一把锁,性能很高。
(12)调度组:在一组异步代码执行完毕后,统一获得通知。类似barrier。
应用场景:加载20条微博,缓存所有图片,拿到本地后知道图片的宽高(网络都是二进制数据),将一组图像异步缓存到本地之后统一获得通知。
调度组一点也没有改变原有的GCD,只是在任务开始时候,进组(加一个标记),然后block最后一句离组。当组没东西就可以发通知了。
- (void)group2 { // 队列 dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // 调度组 dispatch_group_t group = dispatch_group_create(); // 1. 进入群组,给 group 打一个标记,在后续紧接着的 block 归 group 监听 // dispatch_group_enter 和 dispatch_group_leave 必须成对出现! dispatch_group_enter(group); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:0.8]; NSLog(@"download A - %@", [NSThread currentThread]); // 耗时操作.... // block 的最后一句 // 2. 离开群组 dispatch_group_leave(group); }); dispatch_group_enter(group); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:1.0]; NSLog(@"download B - %@", [NSThread currentThread]); // 耗时操作.... // block 的最后一句 // 2. 离开群组 dispatch_group_leave(group); }); // 监听群组完成 dispatch_group_notify - 异步的 // dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // NSLog(@"OK"); // }); // 等待群组空,一直到永远,群组不空,这句代码就死等,同步 dispatch_group_wait(group, DISPATCH_TIME_FOREVER); NSLog(@"OK"); } (13)延迟操作:dispatch_after dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"hey"); });