iOS:dispatch_block_t操作

通过 GCD 提供的方法来将一些需要耗时操作放到非主线程上做,使得 App 能够运行的更加流畅响应更快。但是使用 GCD 时需要注意避免可能引起线程爆炸和死锁的情况,还有非主线程处理任务也不是万能的,如果一个处理需要消耗大量内存或者大量CPU操作 GCD 也没法帮你,只能通过将处理进行拆解分步骤分时间进行处理才比较妥当。

一、dispatch_block_t应用

1、dispatch_block_t初始化

1> dispatch_block_create

dispatch_block_t dispatch_block_create(dispatch_block_flags_t flags, dispatch_block_t block);

flags的类型为 dispatch_block_flags_t 的枚举,用于设置 block 的标记,定义如下:

DISPATCH_ENUM(dispatch_block_flags, unsigned long,
    DISPATCH_BLOCK_BARRIER = 0x1,
    DISPATCH_BLOCK_DETACHED = 0x2,
    DISPATCH_BLOCK_ASSIGN_CURRENT = 0x4,
    DISPATCH_BLOCK_NO_QOS_CLASS = 0x8,
    DISPATCH_BLOCK_INHERIT_QOS_CLASS = 0x10,
    DISPATCH_BLOCK_ENFORCE_QOS_CLASS = 0x20,
);

2> dispatch_block_create_with_qos_class

dispatch_block_t dispatch_block_create_with_qos_class(dispatch_block_flags_t flags,
    dispatch_qos_class_t qos_class, int relative_priority,
    dispatch_block_t block);

这种方式在创建 block 的同时可以指定了相应的优先级。

dispatch_qos_class_tqos_class_t的别名,qos_class_t 是一种枚举,有以下类型:

  • QOS_CLASS_USER_INTERACTIVE:user interactive 等级表示任务需要被立即执行,用来在响应事件之后更新 UI,来提供好的用户体验。这个等级最好保持小规模。
  • QOS_CLASS_USER_INITIATED:user initiated 等级表示任务由 UI 发起异步执行。适用场景是需要及时结果同时又可以继续交互的时候。
  • QOS_CLASS_DEFAULT:default 默认优先级。
  • QOS_CLASS_UTILITY:utility 等级表示需要长时间运行的任务,伴有用户可见进度指示器。经常会用来做计算,I/O,网络,持续的数据填充等任务。这个任务节能。
  • QOS_CLASS_BACKGROUND:background 等级表示用户不会察觉的任务,使用它来处理预加载,或者不需要用户交互和对时间不敏感的任务。
  • QOS_CLASS_UNSPECIFIED:unspecified 未指明
dispatch_queue_t concurrentQuene = dispatch_queue_create("concurrentQuene", DISPATCH_QUEUE_CONCURRENT);

dispatch_block_t block = dispatch_block_create(0, ^{
    NSLog(@"normal do some thing...");
});
dispatch_async(concurrentQuene, block);

//
dispatch_block_t qosBlock = dispatch_block_create_with_qos_class(0, QOS_CLASS_DEFAULT, 0, ^{
    NSLog(@"qos do some thing...");
});
dispatch_async(concurrentQuene, qosBlock);

2、监听 block 执行结束

1> dispatch_block_wait

long dispatch_block_wait(dispatch_block_t block, dispatch_time_t timeout);

注意:因为 dispatch_block_wait 会阻塞当前线程,所以不应该放在主线程中调用。

dispatch_queue_t concurrentQuene = dispatch_queue_create("concurrentQuene", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(concurrentQuene, ^{
    dispatch_queue_t allTasksQueue = dispatch_queue_create("allTasksQueue", DISPATCH_QUEUE_CONCURRENT);

    dispatch_block_t block = dispatch_block_create(0, ^{
        NSLog(@"开始执行");
        [NSThread sleepForTimeInterval:3];
        NSLog(@"结束执行");
    });

    dispatch_async(allTasksQueue, block);
    // 等待时长,10s 之后超时
    dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC));
    long resutl = dispatch_block_wait(block, timeout);
    if (resutl == 0) {
        NSLog(@"执行成功");
    } else {
        NSLog(@"执行超时");
    }
});

2> dispatch_block_notify

void dispatch_block_notify(dispatch_block_t block,     
        dispatch_queue_t queue, 
        dispatch_block_t notification_block);

该函数接收三个参数,第一个参数是需要监视的 block,第二个参数是监听的 block 执行结束之后要提交执行的队列 queue,第三个参数是待加入到队列中的 block。 和 dispatch_block_wait 的不同之处在于:dispatch_block_notify函数不会阻塞当前线程。

NSLog(@"---- 开始设置任务 ----");
dispatch_queue_t serialQueue =   dispatch_queue_create("com.fyf.serialqueue",   DISPATCH_QUEUE_SERIAL);

// 耗时任务
dispatch_block_t taskBlock = dispatch_block_create(0, ^{
    NSLog(@"开始耗时任务");
    [NSThread sleepForTimeInterval:2.f];
    NSLog(@"完成耗时任务");
});

dispatch_async(serialQueue, taskBlock);

// 更新 UI
dispatch_block_t refreshUI = dispatch_block_create(0, ^{
    NSLog(@"更新 UI");
});

// 设置监听
dispatch_block_notify(taskBlock, dispatch_get_main_queue(), refreshUI);
NSLog(@"---- 完成设置任务 ----");

3、取消block

dispatch_block_cancel

void dispatch_block_cancel(dispatch_block_t block);

取消操作使将来执行 dispatch block 立即返回,但是对已经在执行的 dispatch block 没有任何影响。

二、性能优化相关应用

1> 异步处理事件

2> 需要耗时长的任务

GCD 的 block 通过 dispatch_block_create_with_qos_class 方法指定队列的 QoS 为 QOS_CLASS_UTILITY。这种 QoS 系统会针对大的计算,I/O,网络以及复杂数据处理做电量优化。

3> 避免线程爆炸

举个例子,下面的写法就比较危险,可能会造成线程爆炸和死锁:

for (int i = 0; i < 999; i++) { 
dispatch_async(q, ^{…}); 
} 
dispatch_barrier_sync(q, ^{});

1、使用串行队列
2、使用 NSOperationQueues 的并发限制方法NSOperationQueue.maxConcurrentOperationCount
3、使用GCD信号量dispatch_semaphore控制线程最大并发

4> I/O 性能优化

一般出发点:

1、将零碎的内容作为一个整体进行写入 ;
2、使用合适的 I/O 操作 API ;
3、使用合适的线程 ;
4、使用 NSCache 做缓存能够减少 I/O;
截屏2020-11-06 下午5.56.22.png

参考链接:
深入剖析 iOS 性能优化
GCD 之任务操作(Dispatch Block)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335