1.在ARC中为什么block会导致循环引用
我们知道在一个类中定义了一个block的属性值completionHandler,那么这个类就对completionHandler拥有所有权,也就是对这个completionHandler强引用,如果在completionHandler块语句里面使用self.xx,此时这个block块就对self拥有了所有权,也就是对当前类强引用,这样就会造成循环引用,导致当前类无法释放。对于解决方法,我们看苹果的官方文档是这样讲的:
在手动引用技术的模式下, __block id x,不会保留x。在ARC模式下, __block id x;默认会保留x(就像其它值一样)。为了在ARC下达到手动引用技术的效果,你可以使用 __unsafe__unretained __block id x ;然而,就像 __unsafe_unretained名字蕴含的那样,拥有一个未保留变量(non-retained variable)是危险的(因为他可能是一个野指针),因此最好不要使用。有两个更好的选择是要么使用 __weak(如果你不需要支持IOS4或OS X v10.6),要么设置 __block值nil来打破循环引用。
如上所述,你可以使用 __block限定符来替代,并在 completion处理方法中将myController的值设置为nil:
或者,你可以使用一个临时的 __weak变量。以下代码列举了一个简单的实现:
2.关于gcd队列
并发队列:允许多个任务同时执行。
串行队列:任务按顺序依次执行。
//全局队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
/*任务a */
NSLog(@"任务a完成\n");
NSLog(@"任务a完成\n");
NSLog(@"任务a完成\n");
NSLog(@"任务a完成\n");
});
NSLog(@"先完成a\n");
dispatch_group_async(group, queue, ^{
/*任务b */
NSLog(@"任务b完成\n");
NSLog(@"任务b完成\n");
NSLog(@"任务b完成\n");
NSLog(@"任务b完成\n");
});
NSLog(@"先完成b\n");
dispatch_group_async(group, queue, ^{
/*任务c */
NSLog(@"任务c完成\n");
});
NSLog(@"先完成c\n");
dispatch_group_async(group, queue, ^{
/*任务d */
NSLog(@"任务d完成\n");
});
NSLog(@"先完成d\n");
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 在a、b、c、d异步执行完成后,会回调这里
NSLog(@"任务a,b,c,d完成\n");
});
下面我们查看下打印结果:
2017-11-07 14:18:23.774764+0800 TabbariOSTest[2789:742111] 先完成a
2017-11-07 14:18:23.774812+0800 TabbariOSTest[2789:742801] 任务a完成
2017-11-07 14:18:23.774904+0800 TabbariOSTest[2789:742111] 先完成b
2017-11-07 14:18:23.774910+0800 TabbariOSTest[2789:742801] 任务a完成
2017-11-07 14:18:23.774920+0800 TabbariOSTest[2789:742651] 任务b完成
2017-11-07 14:18:23.775036+0800 TabbariOSTest[2789:742111] 先完成c
2017-11-07 14:18:23.775042+0800 TabbariOSTest[2789:742801] 任务a完成
2017-11-07 14:18:23.775046+0800 TabbariOSTest[2789:742650] 任务c完成
2017-11-07 14:18:23.775062+0800 TabbariOSTest[2789:742651] 任务b完成
2017-11-07 14:18:23.775134+0800 TabbariOSTest[2789:742801] 任务a完成
2017-11-07 14:18:23.775144+0800 TabbariOSTest[2789:742111] 先完成d
2017-11-07 14:18:23.775179+0800 TabbariOSTest[2789:742803] 任务d完成
2017-11-07 14:18:23.775755+0800 TabbariOSTest[2789:742651] 任务b完成
2017-11-07 14:18:23.776168+0800 TabbariOSTest[2789:742803]
2017-11-07 14:18:23.776619+0800 TabbariOSTest[2789:742651] 任务b完成
2017-11-07 14:18:23.778079+0800 TabbariOSTest[2789:742111] 任务a,b,c,d完成
2017-11-07 14:18:23.778181+0800 TabbariOSTest[2789:742111]
全局队列队列其实是并发队列,并由整个进程共享,提交到全局队列之后能够立马运行,它的质量分以下四种:
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
但是如果我们想让以上异步请求顺序执行,那么我们就需要将全局队列改成用户队列,自己创建的用户队列有以下几种:
/*!
* @const DISPATCH_QUEUE_SERIAL
*
* @discussion A dispatch queue that invokes blocks serially in FIFO order.
*/
#define DISPATCH_QUEUE_SERIAL NULL
/*!
* @const DISPATCH_QUEUE_SERIAL_INACTIVE
*
* @discussion
* A dispatch queue that invokes blocks serially in FIFO order, and that is
* created initially inactive. See dispatch_queue_attr_make_initially_inactive().
*/
#define DISPATCH_QUEUE_SERIAL_INACTIVE \
dispatch_queue_attr_make_initially_inactive(DISPATCH_QUEUE_SERIAL)
/*!
* @const DISPATCH_QUEUE_CONCURRENT
*
* @discussion A dispatch queue that may invoke blocks concurrently and supports
* barrier blocks submitted with the dispatch barrier API.
*/
#define DISPATCH_QUEUE_CONCURRENT \
DISPATCH_GLOBAL_OBJECT(dispatch_queue_attr_t, \
_dispatch_queue_attr_concurrent)
对上面的定义解释依次为:
DISPATCH_QUEUE_SERIAL:
一个调度队列, 它以 FIFO 顺序依次调用块。
DISPATCH_QUEUE_SERIAL_INACTIVE:
一个调度队列, 它以 FIFO 顺序依次调用块, 并在最初创建时不活动。见 dispatch_queue_attr_make_initially_inactive ()。
DISPATCH_QUEUE_CONCURRENT:
并发队列是这样的,可以同时调用各语句块,且可以通过dispatch_barrier_async这样的api来做些限制。
即:
dispatch_queue_t userQueue = dispatch_queue_create("com.iOS.gcd", DISPATCH_QUEUE_SERIAL);
下面为打印结果:
2017-11-07 14:14:27.442110+0800 TabbariOSTest[2747:727916] 先完成a
2017-11-07 14:14:27.442143+0800 TabbariOSTest[2747:728085] 任务a完成
2017-11-07 14:14:27.442316+0800 TabbariOSTest[2747:727916] 先完成b
2017-11-07 14:14:27.442430+0800 TabbariOSTest[2747:728085] 任务a完成
2017-11-07 14:14:27.442433+0800 TabbariOSTest[2747:727916] 先完成c
2017-11-07 14:14:27.443006+0800 TabbariOSTest[2747:728085] 任务a完成
2017-11-07 14:14:27.443011+0800 TabbariOSTest[2747:727916] 先完成d
2017-11-07 14:14:27.443612+0800 TabbariOSTest[2747:728085] 任务a完成
2017-11-07 14:14:27.444575+0800 TabbariOSTest[2747:728085] 任务b完成
2017-11-07 14:14:27.445085+0800 TabbariOSTest[2747:728085] 任务b完成
2017-11-07 14:14:27.445205+0800 TabbariOSTest[2747:728085] 任务b完成
2017-11-07 14:14:27.445351+0800 TabbariOSTest[2747:728085] 任务b完成
2017-11-07 14:14:27.445508+0800 TabbariOSTest[2747:728085] 任务c完成
2017-11-07 14:14:27.445784+0800 TabbariOSTest[2747:728085] 任务d完成
2017-11-07 14:14:27.446300+0800 TabbariOSTest[2747:728085]
2017-11-07 14:14:27.446877+0800 TabbariOSTest[2747:727916] 任务a,b,c,d完成
2017-11-07 14:14:27.446994+0800 TabbariOSTest[2747:727916]
由此可见,虽然代码会顺序执行,但是队列里的任务执行顺序会串行执行。将DISPATCH_QUEUE_SERIAL改为DISPATCH_QUEUE_CONCURRENT之后我们发现情况与全局队列情况一致。
有关dispatch group的定义,官方文档这样描述:
Submits a block to a dispatch queue and associates the block with the specified dispatch group.
向调度队列提交块, 并将该块与指定的调度组关联。
Submits a block to a dispatch queue and associates the block object with the given dispatch group. The dispatch group can be used to wait for the completion of the block objects it references.
向调度队列提交块, 并将该块对象与给定的调度组相关联。调度组可用于等待它所引用的块对象的完成。
我理解为它的出现是为了将块语句提交到队列,并能更好的检测块对象。
3.有关同步、异步执行
看如下代码片段:
dispatch_queue_t queue = dispatch_queue_create("com.apple.ios", DISPATCH_QUEUE_SERIAL);
NSLog(@"开始执行任务");
dispatch_async(queue, ^{
NSLog(@"任务执行完成");
});
NSLog(@"任务结尾处");
dispatch_asynch代表异步执行,我们查看结果:
2017-11-07 14:30:59.971841+0800 TabbariOSTest[2903:782727] 开始执行任务
2017-11-07 14:30:59.971989+0800 TabbariOSTest[2903:782727] 任务结尾处
2017-11-07 14:30:59.972008+0800 TabbariOSTest[2903:783079] 任务执行完成
可见异步执行任务,不能任务执行完毕就接着往下执行。
再看同步执行:
dispatch_queue_t queue = dispatch_queue_create("com.apple.ios", DISPATCH_QUEUE_SERIAL);
NSLog(@"开始执行任务");
dispatch_sync(queue, ^{
NSLog(@"任务执行完成");
});
NSLog(@"任务结尾处");
执行结果如下:
2017-11-07 14:34:24.314269+0800 TabbariOSTest[2948:795914] 开始执行任务
2017-11-07 14:34:24.314446+0800 TabbariOSTest[2948:795914] 任务执行完成
2017-11-07 14:34:24.314560+0800 TabbariOSTest[2948:795914] 任务结尾处
同步执行要等到任务结束才会继续往下执行。
4.关于dispatch_barrier_async的使用
在任务 代码片段 NSLog(@”先完成b\n”) 与 创建任务c之间添加如下代码:
dispatch_barrier_sync(queue, ^{
NSLog(@"暂停并发队列执行\n");
NSLog(@"暂停并发队列执行\n");
NSLog(@"暂停并发队列执行\n");
});
查看打印结果:
2017-11-07 14:55:01.395284+0800 TabbariOSTest[3138:866694] 先完成a
2017-11-07 14:55:01.395287+0800 TabbariOSTest[3138:866860] 任务a完成
2017-11-07 14:55:01.395460+0800 TabbariOSTest[3138:866860] 任务a完成
2017-11-07 14:55:01.395462+0800 TabbariOSTest[3138:866694] 先完成b
2017-11-07 14:55:01.395479+0800 TabbariOSTest[3138:866857] 任务b完成
2017-11-07 14:55:01.395537+0800 TabbariOSTest[3138:866860] 任务a完成
2017-11-07 14:55:01.395571+0800 TabbariOSTest[3138:866857] 任务b完成
2017-11-07 14:55:01.395603+0800 TabbariOSTest[3138:866860] 任务a完成
2017-11-07 14:55:01.395691+0800 TabbariOSTest[3138:866857] 任务b完成
2017-11-07 14:55:01.395941+0800 TabbariOSTest[3138:866857] 任务b完成
2017-11-07 14:55:01.396421+0800 TabbariOSTest[3138:866694] 暂停并发队列执行
2017-11-07 14:55:01.396614+0800 TabbariOSTest[3138:866694] 暂停并发队列执行
2017-11-07 14:55:01.396797+0800 TabbariOSTest[3138:866694] 暂停并发队列执行
2017-11-07 14:55:01.396956+0800 TabbariOSTest[3138:866694] 先完成c
2017-11-07 14:55:01.396967+0800 TabbariOSTest[3138:866857] 任务c完成
2017-11-07 14:55:01.397156+0800 TabbariOSTest[3138:866694] 先完成d
2017-11-07 14:55:01.397177+0800 TabbariOSTest[3138:866858] 任务d完成
2017-11-07 14:55:01.400790+0800 TabbariOSTest[3138:866694] 任务a,b,c,d完成
由结果可知,dispatch_barrier_sync只是为了在并行队列中插入一段任务,等到前面的任务结束才会执行下面的并发任务。