`
shfzhzhr
  • 浏览: 70580 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Blocks与Dispatch Queue的使用

    博客分类:
  • IOS
 
阅读更多

 

 

block是什么
block是一个C level的语法以及运行时的一个特性,和标准C中的函数(函数指针)类似。用于回调函数的地方。两个对象间的通讯。实现轻量级的“代理”。


blocks和C语言函数指针的区别

Blocks应用 - 阿帕奇 - XGG  XGG

 

如何调用blocks
调用block和C语言函数指针的调用一模一样

Blocks应用 - 阿帕奇 - XGG  XGG

 

Blocks应用 - 阿帕奇 - XGG  XGG

 
如何在 block 中修改外部变量?????
考虑到 block 的目的是为了支持并行编程,对于普通的 local 变量,我们就不能在 block 里面随意修改(原因很简单,block 可以被多个线程并行运行,会有问题的),而且如果你在 block 中修改普通的 local 变量,编译器也会报错。那么该如何修改外部变量呢?有两种办法,第一种是可以修改 static 全局变量;第二种是可以修改用新关键字 __block 修饰的变量
__block关键字
一 个Block的内部是可以引用自身作用域外的变量的,包括static变量,extern变量或自由变量(定义一个变量的时候,如果不加存储修饰符,默认 情况下就是自由变量auto,auto变量保存在stack中的,除了auto之外还存在register,static等存储修饰符),
对于局部变量,在block中是只读的。在引入block的同时,还引入了一种特殊的关键字__block,用此声明一个局部变量可以被函数块修改。


实例:
void(^aBlock)(void) = 0;          // 声明一个block
    aBlock = ^(void){                 // 给block赋值
        NSLog(@"this is a block.");
    };
    aBlock();                            // 执行block


上面我们介绍了 block 及其基本用法,但还没有涉及并行编程。 block 与 Dispatch Queue 分发队列结合起来使用,是 iOS 中并行编程的利器。

    NSAutoreleasePool *pool = [[NSAutoreleasePoolalloc]init];

    

    // 创建一个串行分发队列

    dispatch_queue_t queue = dispatch_queue_create("studyBlocks", NULL);

    

    // 将一个 block 任务加入到其中并行运行. 这样 block 就会在新的线程中运行,直到结束返回主线程

    // 加入 dispatch_queue 中的 block 必须是无参数也无返回值的

    dispatch_async(queue, ^(void){

        int sum = 0;

        for (int i = 0; i<100; i++) {

            sum += i;

        }

        NSLog(@"sum:%d",sum);

    });

 

    dispatch_release(queue);

    [pool drain];

1、dispatch_queue_t 类型 的定义如下:
typedef void (^dispatch_block_t)( void);
这意味着加入 dispatch_queue 中的 block 必须是无参数也无返回值的

2、dispatch_queue_create 函数 的定义如下:
dispatch_queue_t  dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
函数创建分发队列dispatch_queue。带有两个参数:一个用于标识 dispatch_queue 的字符串;一个是保留的 dispatch_queue 属性,将其设置为 NULL 即可。
3、也可使用函数 dispatch_queue_t dispatch_get_global_queue(long priority, unsigned long flags);
来获得全局的 dispatch_queue,参数 priority 表示优先级,值得注意的是:我们不能修改该函数返回的 dispatch_queue。例如:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

[[[selfcaptureManager] session] startRunning];

});

4、dispatch_async 函数的定义如下:
void  dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
该函数将一个 block 加入一个 dispatch_queue,这个 block 会再其后得到调度时,并行运行。
相应的 dispatch_sync 函数就是同步执行了,一般很少用到。比如上面的代码如果我们修改为 dispatch_sync,那么就无需编写 flag 同步代码了。
5、dispatch_block_t 类型
dispatch_block_t

dispatch_queue 的运作机制及线程间同步
我们可以将许多 blocks 用 dispatch_async 函数提交到到 dispatch_queue 串行运行。这些 blocks 是按照 FIFO(先入先出)规则调度的,也就是说,先加入的先执行,后加入的一定后执行,但在某一个时刻,可能有多个 block 同时在执行
在上面的例子中,我们的主线程一直在轮询 flag 以便知晓 block 线程是否执行完毕,这样做的效率是很低的,严重浪费 CPU 资源。我们可以使用一些通信机制来解决这个问题,如:semaphore(信号量)。 semaphore 的原理很简单,就是生产-消费模式,必须生产一些资源才能消费,没有资源的时候,那我就啥也不干,直到资源就绪。

    NSAutoreleasePool * pool = [[NSAutoreleasePoolalloc] init];

    initData();

    

    // Create a semaphore with 0 resource

    __block dispatch_semaphore_t sem = dispatch_semaphore_create(0);

    

    // create dispatch semaphore

    dispatch_queue_t queue = dispatch_queue_create("StudyBlocks", NULL);    dispatch_async(queue, ^(void) {

        int sum = 0;

        for(int i = 0; i < Length; i++)

            sum += data;

        NSLog(@" >> Sum: %d", sum);

        // signal the semaphore: add 1 resource

        dispatch_semaphore_signal(sem);

    });

    

    // wait for the semaphore: wait until resource is ready.

    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);

    

    dispatch_release(sem);

    dispatch_release(queue);

    

    [pool drain];

1、dispatch_semaphore_create函数
此函数用于创建一个 __block semaphore,这里将其资源初始值设置为 0 (不能少于 0),表示任务还没有完成,没有资源可用主线程不要做事情。
2、dispatch_semaphore_signal函数
该函数 增加 semaphore 计数(可理解为资源数),表明任务完成,有资源可用主线程可以做事情了。
3、dispatch_semaphore_wait函数
主线程中的 dispatch_semaphore_wait 就是减少 semaphore 的计数,如果资源数少于 0,则表明资源还可不得,我得按照FIFO(先等先得)的规则等待资源就绪,一旦资源就绪并且得到调度了,我再执行。

下面我们来看一个按照 FIFO 顺序执行并用 semaphore 同步的例子:先将数组求和再依次减去数组。

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    initData();

    

    __block int sum = 0;

    

    // Create a semaphore with 0 resource

    __block dispatch_semaphore_t sem = dispatch_semaphore_create(0);

    __blockdispatch_semaphore_t taskSem = dispatch_semaphore_create(0);

    

    // create dispatch semaphore

    dispatch_queue_t queue = dispatch_queue_create("StudyBlocks", NULL);

    

    dispatch_block_t task1 = ^(void) {

        int s = 0;

        for (int i = 0; i < Length; i++)

            s += data;

        sum = s;

        

        NSLog(@" >> after add: %d", sum);

        

        dispatch_semaphore_signal(taskSem);

    };

    

    dispatch_block_t task2 = ^(void) {

        dispatch_semaphore_wait(taskSem, DISPATCH_TIME_FOREVER);

        

        int s = sum;

        for (int i = 0; i < Length; i++)

            s -= data;

        sum = s;

        

        NSLog(@" >> after subtract: %d", sum);

        dispatch_semaphore_signal(sem);

    };

    

    dispatch_async(queue, task1);

    dispatch_async(queue, task2);

    

    // wait for the semaphore: wait until resource is ready.

    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);

    

    dispatch_release(taskSem);

    dispatch_release(sem);

    dispatch_release(queue);

    

    [pool drain];

 

在 上面的代码中,我们利用了 dispatch_queue 的 FIFO 特性,确保 task1 先于 task2 执行,而 task2 必须等待直到 task1 执行完毕才开始干正事,主线程又必须等待 task2 才能干正事。 这样我们就可以保证先求和,再相减,然后再让主线程运行结束这个顺序。


使用 dispatch_apply 进行并发迭代:

对于上面的求和操作,我们也可以使用 dispatch_apply 来简化代码的编写.

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    

    initData();

    

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    

    __block int sum = 0;

    __block int *pArray = data;

    

    // iterations

    //

    dispatch_apply(Length, queue, ^(size_t i) {

        sum += pArray;

    });

    

    NSLog(@" >> sum: %d", sum);

    

    dispatch_release(queue);

    

    [pool drain];

 
注意,这里使用了全局 dispatch_queue。
dispatch_apply 的定义如下:
dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));
参数 iterations 表示迭代的次数,void (^block)(size_t) 是 block 循环体。这么做与 for 循环相比有什么好处呢?答案是:并行,这里的求和是并行的,并不是按照顺序依次执行求和的。

dispatch group

我们可以将完成一组相关任务的 block 添加到一个 dispatch group 中去,这样可以在 group 中所有 block 任务都完成之后,再做其他事情。比如 6 中的示例也可以使用 dispatch group 实现:

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    

    initData();

    

    __block int sum = 0;

    

    // Create a semaphore with 0 resource

    //

    __block dispatch_semaphore_t taskSem = dispatch_semaphore_create(0);

    

    // create dispatch semaphore

    //

    dispatch_queue_t queue = dispatch_queue_create("StudyBlocks", NULL);

    dispatch_group_t group = dispatch_group_create();

    

    dispatch_block_t task1 = ^(void) {

        int s = 0;

        for (int i = 0; i < Length; i++)

            s += data;

        sum = s;

        

        NSLog(@" >> after add: %d", sum);

        

        dispatch_semaphore_signal(taskSem);

    };

    

    dispatch_block_t task2 = ^(void) {

        dispatch_semaphore_wait(taskSem, DISPATCH_TIME_FOREVER);

        

        int s = sum;

        for (int i = 0; i < Length; i++)

            s -= data;

        sum = s;

        

        NSLog(@" >> after subtract: %d", sum);

    };

    

    // Fork

    dispatch_group_async(group, queue, task1);

    dispatch_group_async(group, queue, task2);

    

    // Join

    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

    

    dispatch_release(taskSem);

    dispatch_release(queue);

    dispatch_release(group);

    

    [pool drain];

 

在上面的代码中,我们使用 dispatch_group_create 创建一个 dispatch_group_t,然后使用语句:dispatch_group_async(group, queue, task1)将 block 任务加入队列中,并与组关联,这样我们就可以使用 dispatch_group_wait(group, DISPATCH_TIME_FOREVER)来等待组中所有的 block 任务完成再继续执行

至此我们了解了 dispatch queue 以及 block 并行编程相关基本知识,开始在项目中运用它们吧。

分享到:
评论

相关推荐

    blocks编程要点

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 这里可以执行耗时的操作 }); ``` ### 4.2 __BLOCK与线程安全 `__BLOCK`关键字还可以用来确保在多线程环境中对共享变量的...

    ios Blocks应用

    例如,在GCD(Grand Central Dispatch)中,我们经常使用Blocks来执行后台任务: ```objc dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 后台任务 NSLog(@"Background ...

    斯坦福大学开放课程:iOS开发教程2010年秋(Lecture 13)

    - **同步执行**:使用`dispatch_sync`函数可以在指定队列中同步执行Blocks,即主调线程会等待Blocks执行完成。 - **示例**: ```objective-c dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_...

    Blocks链式编程基础demo

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ // 异步执行的代码 }); ``` ### 5. 链式编程与Blocks 链式编程是一种编程风格,使得方法...

    iOS 并发编程

    DispatchQueue是GCD中用于管理任务队列的概念。它有两个主要类型:串行队列(Serial Queue)和并发队列(Concurrent Queue)。串行队列保证任务按照添加的顺序依次执行,而并发队列则允许任务同时执行。开发者需要...

    核心动画、多线程编程、blocks编程要点、pdf 文件绝对能帮到你

    在iOS开发中,掌握核心动画(Core Animation)、多线程编程和Blocks的使用是至关重要的。这些技术不仅可以提升应用性能,还能实现丰富的用户界面和流畅的用户体验。 **1. 核心动画(Core Animation)** Core ...

    NSThread GCD

    在iOS和Mac开发中,NSThread与GCD(Grand Central Dispatch)是两种主要的多线程编程技术。本文将深入探讨这两个概念,并通过实例代码帮助初学者理解它们的工作原理及如何在实践中应用。 首先,NSThread是Objective...

    block的学习demo(回调,基本用法)

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 后台执行耗时操作 NSString *result = ...; dispatch_async(dispatch_get_main_queue(), ^{ // 主线程更新UI self....

    IOS应用源码——Threading.rar

    - GCD的实践,例如使用Dispatch Queue执行同步和异步任务,使用Dispatch Group等待多个任务完成,或者使用Dispatch Barrier确保某些操作在所有其他任务完成后执行。 通过分析这些源码,开发者可以深入理解iOS线程...

    Pro Multithreading and Memory Management for iOS

    - **Blocks实现**:第五章进一步探讨了Blocks的底层实现机制,这对于理解Blocks如何与其他技术(如GCD)结合使用至关重要。 #### 3. Grand Central Dispatch (GCD):高效的多线程处理 Grand Central Dispatch (GCD...

    GCD.rar_kjnkjnkj

    8. **GCD与Blocks**:GCD广泛使用blocks作为任务的封装方式,这使得代码更加简洁和易于理解。 9. **性能优化**:GCD能够智能地调度任务,充分利用多核处理器的优势,提高程序的执行效率。 10. **GCD与Operation ...

    Object-C语言入门教程&代码示例 .zip

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ // 执行后台任务 dispatch_sync(dispatch_get_main_queue(), ^{ // 更新UI }); }); ``` ...

    Swift5.2 Block(闭包)

    在Objective-C(OC)编程中,我们使用blocks来实现类似的功能。尽管Swift和OC的语法有所不同,但它们在概念上是相似的。Swift的闭包提供了一种更安全、更强大的方式来处理代码块,同时保持了Swift的类型安全性和简洁...

    Bolcks 各种用法

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 在这里执行耗时操作 NSLog(@"这是后台任务"); dispatch_async(dispatch_get_main_queue(), ^{ // 回到主线程更新UI ...

    详解iOS多线程GCD的使用

    Grand Central Dispatch(GCD)是异步执行任务的技术之一 dispatch queue分成以下三种: 1)运行在主线程的Main queue,通过dispatch_get_main_queue获取。...* In order to invoke blocks submitted to the main queue

    iOS block使用总结

    通过`dispatch_queue_t`创建队列,然后使用`dispatch_async`或`dispatch_sync`提交Block到队列,可以轻松地实现异步或同步操作。 7. **循环引用问题** 当Block作为对象的属性或者成员变量时,如果不小心可能会引起...

    iOSGCDandBlock:iOS的GCD多执行绪更新主执行绪上的UI控制项状态

    可以使用`dispatch_queue_create()`创建自定义序列队列,或使用全局序列队列(例如` DISPATCH_QUEUE_SERIAL`)。 - **Concurrent Queues**:并行队列允许多个任务同时执行。系统提供全局并行队列(例如`DISPATCH_...

    Objective-C2.0程序设计习题官方答案

    了解如何使用`dispatch_queue`、`dispatch_async`和`dispatch_sync`等函数。 8. **KVC(Key-Value Coding)**:一种间接访问对象属性的方式,无需直接调用setter和getter方法。理解KVC的关键字`@keyPath`,以及如何...

    Effective Objective-C 2.0.pdf

    - 学习如何使用`dispatch_queue_t`、`dispatch_group_t`和`dispatch_barrier_async`等API来优化并行处理。 10. **错误处理(Error Handling)**: - Objective-C中的错误处理通常使用`@try`、`@catch`、`@throw`...

    block块精解

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 异步执行的任务 }); ``` ### 9. Block与操作符`@autoreleasepool` 在Block内部,系统会自动创建一个`@autoreleasepool`,...

Global site tag (gtag.js) - Google Analytics