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 永远都只会独自执行.





采用PyQt5框架与Python编程语言构建图书信息管理平台 本项目基于Python编程环境,结合PyQt5图形界面开发库,设计实现了一套完整的图书信息管理解决方案。该系统主要面向图书馆、书店等机构的日常运营需求,通过模块化设计实现了图书信息的标准化管理流程。 系统架构采用典型的三层设计模式,包含数据存储层、业务逻辑层和用户界面层。数据持久化方案支持SQLite轻量级数据库与MySQL企业级数据库的双重配置选项,通过统一的数据库操作接口实现数据存取隔离。在数据建模方面,设计了包含图书基本信息、读者档案、借阅记录等核心数据实体,各实体间通过主外键约束建立关联关系。 核心功能模块包含六大子系统: 1. 图书编目管理:支持国际标准书号、中国图书馆分类法等专业元数据的规范化著录,提供批量导入与单条录入两种数据采集方式 2. 库存动态监控:实时追踪在架数量、借出状态、预约队列等流通指标,设置库存预警阈值自动提醒补货 3. 读者服务管理:建立完整的读者信用评价体系,记录借阅历史与违规行为,实施差异化借阅权限管理 4. 流通业务处理:涵盖借书登记、归还处理、续借申请、逾期计算等标准业务流程,支持射频识别技术设备集成 5. 统计报表生成:按日/月/年周期自动生成流通统计、热门图书排行、读者活跃度等多维度分析图表 6. 系统维护配置:提供用户权限分级管理、数据备份恢复、操作日志审计等管理功能 在技术实现层面,界面设计遵循Material Design设计规范,采用QSS样式表实现视觉定制化。通过信号槽机制实现前后端数据双向绑定,运用多线程处理技术保障界面响应流畅度。数据验证机制包含前端格式校验与后端业务规则双重保障,关键操作均设有次确认流程。 该系统适用于中小型图书管理场景,通过可扩展的插件架构支持功能模块的灵活组合。开发过程中特别注重代码的可维护性,采用面向对象编程范式实现高内聚低耦合的组件设计,为后续功能迭代奠定技术基础。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值