iOS各类问题汇总(一)

本文介绍了iOS开发中的常见问题,包括ARC中的block导致的循环引用及其解决方法,深入解析GCD的并发队列与串行队列,以及同步、异步执行的区别。此外,还探讨了dispatch_barrier_async的使用场景,帮助开发者更好地理解和应用这些概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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只是为了在并行队列中插入一段任务,等到前面的任务结束才会执行下面的并发任务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值