GCD 使用总结(二)

GCD 使用总结(一) 介绍了 GCD 大概情况,现在看下在项目应用中,哪一些地方用到了 GCD 的能力.


1.延后加载 >>> dispatch_after

举例:在某些时候,我们加载一个 view, 希望用户注意到我们想要突出的部分,但是 view 上的东西太多,一起加载出现,用户很有可能会错过我们想要提醒的部分.
使用 dispatch_after  可以实现这个功能.   dispatch_after能够延后执行代码. 将需要的 view 先加载出来, 在加载其他 view. 这样用户就能注意到我们想要提醒的部分了.
 UIView *view1 = [[UIView alloc]initWithFrame:CGRectMake(60, 60, 40, 40)];
    UIView *view2 = [[UIView alloc]initWithFrame:CGRectMake(60, 260, 40, 40)];
    UIView *view3 = [[UIView alloc]initWithFrame:CGRectMake(160, 60, 40, 40)];
    UIView *view4 = [[UIView alloc]initWithFrame:CGRectMake(160, 260, 40, 40)];
    view1.backgroundColor = [UIColor greenColor];
    view2.backgroundColor = [UIColor greenColor];
    view3.backgroundColor = [UIColor greenColor];
    view4.backgroundColor = [UIColor greenColor];
    
    UIView *centerView = [[UIView alloc]initWithFrame:CGRectMake(110, 160, 40, 40)];
    centerView.backgroundColor = [UIColor redColor];
    
    double delayInSeconds = 2.0;
//    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); // 1
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ // 2

        NSLog(@"当前线程:%@",[NSThread currentThread]);
        [self addSubview:view1];
        [self addSubview:view2];
        [self addSubview:view3];
        [self addSubview:view4];
    });
    NSLog(@"主线程:%@",[NSThread currentThread]);

    [self addSubview:centerView];

界面比较粗燥,但是不重要.  红色的方块先出现,然后在加载四个绿色的方块. 

2.异步执行

异步执行可以让 CPU 执行更多的事情,让 APP 跑起来更流畅, 本质上就是开辟子线程,让 CPU 合理的干更多的活
举例: 网络请求
 绝大多数网络请求都是耗时操作,尤其下载音视频,图片等. 如果用主线程做这些事情就比较蠢了.

    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_async(globalQueue, ^{
        // 一个异步的任务,如网络请求,耗时的文件操作等等
//        ...
        NSLog(@"当前线程:%@",[NSThread currentThread]);
        dispatch_async(dispatch_get_main_queue(), ^{
            // UI刷新 或其他主线程操作
//            ...
        });
    });


    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_async(globalQueue, ^{
        // 一个异步的任务,如网络请求,耗时的文件操作等等
//        ...
        NSLog(@"当前线程:%@",[NSThread currentThread]);
        dispatch_async(dispatch_get_main_queue(), ^{
            // UI刷新 或其他主线程操作
//            ...
        });
    });

3.单例安全

不知道为什么,单例在 ios 的开发中多的可怕.
单例的特点之一就是 在程序运行的过程中一直存在,且唯一.

+ (instancetype)sharedManager
{
    static PhotoManager *sharedPhotoManager = nil;
    if (!sharedPhotoManager) {
        sharedPhotoManager = [[PhotoManager alloc] init];
    }
    return sharedPhotoManager;
}
像这种通过断定 存不存在来决定是否创建,看起来没毛病. 但是在单例在多线程调用的时候就会有问题.
简单的举例就是在第一次调用创建单例的时候 [alloc init]的时候 还没进行,第二次调用就触发又会走进 [alloc init]的方法.这样就会创建两个单例. 单例这就不唯一了;
如下代码:
+ (instancetype)sharedManager
{
    static PhotoManager *sharedPhotoManager = nil;
    if (!sharedPhotoManager) {
        [NSThread sleepForTimeInterval:2];
        sharedPhotoManager = [[PhotoManager alloc] init];
        NSLog(@"Singleton has memory address at: %@", sharedPhotoManager);
        [NSThread sleepForTimeInterval:2];
        sharedPhotoManager->_photosArray = [NSMutableArray array];
    }
    return sharedPhotoManager;
}


// 外部调用单例的代码
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    [PhotoManager sharedManager];
});
 
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    [PhotoManager sharedManager];
});
如果外部此时同事调用两次单例的创建方法,就会创建两个单例.  手敲代码 打印一遍两个单例的地址 就可以发现地址不同.

这就是多线程 临界区 的问题. 像这样某一段代码被两个线程执行,就会产生不可预知的事情

GCD可以解决这个问题.
+ (instancetype)sharedManager
{
    static PhotoManager *sharedPhotoManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedPhotoManager = [[PhotoManager alloc] init];
    });
    return sharedPhotoManager;
}

dispatch_once_t //一个整形数据 , 
 dispatch_once(&onceToken, ^{//  dispatch_once在运行的时候,取onceToken的值,0就执行 block  ,非零就跳过. 所以这个也重新创建单例提供了可能,
                              // 必要的时候 清零 onceToken 重新创建 单例
 });

4.处理读者 和 写者的问题

线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。

类似对于一个可变内存,多个线程读取没什么问题,但是当读取的时候 另一个线程进行修改时,就很危险了. 而且目前多数单例的可变属性,都没有做到保护.

通过 GCD 的栅栏(dispatch barriers), 可以有效防止这类读取可能被其他线程修改的数据时,导致的不可思议的问题.

- (void)addPerson:(Person *)person
{
    if (person) { // 1
        dispatch_barrier_async(self.concurrentPersonQueue, ^{ // 2
            
            [_personArr addObject:person]; // 3
            dispatch_async(dispatch_get_main_queue(), ^{ // 4

            });
        });
    }
}

self.concurrentPersonQueue  // 初始化为异步的并发队列
在这个并发队列中, barrier 持有的 block 永远都只会独自执行.





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值