浅析block

//
//  main.m
//  07-Block 演练
//
//  Created by LNJ on 14-6-25.
//  Copyright (c) 2014 itcast. All rights reserved.
//

#import<Foundation/Foundation.h>

voidblockDemo2();
voidblockDemo3();
voidblockDemo4();

intmain(intargc,constchar* argv[])
{

   
@autoreleasepool{
       
NSArray*array =@[@(5),@(2),@(3),@(1),@(6)];
       
       
//给数组排序,块代码的返回值,比较结果
       
// NSOrderedAscending = -1L,
       
// NSOrderedSame = 0,
       
// NSOrderedDescending = 1
       
NSArray*result = [arraysortedArrayUsingComparator:^NSComparisonResult(idobj1,idobj2) {
           
// 5, 2, 3, 1, 6
           
// 2, 5, 3, 1, 6
           
// 2, 5, 1, 3, 6
           
// 2, 5, 1, 3, 6
           
// 1, 5, 2, 3, 6
           
// 1, 5, 2, 3, 6
           
// 1, 2, 3, 5, 6
           
NSLog(@"%@ - %@", obj1, obj2);
           
           
//升序排序
//            return [obj1 compare:obj2];
           
//降序排序
//            return [obj2 compare:obj1];
           
//乱序,随机返回-1, 0, 1
           
// 0~2随机数- 1

// 随机越大  把随机数变大点
            return arc4random_uniform( 3) - 1;
         return ( arc4random_uniform( 100) % 3 - 1);
        }];
       
        NSLog( @"==== %@", result);
    }
    return 0;
}


/** 两种数组遍历的对比 */
void blockDemo5()
{
    NSArray *array = @[@(1), @(2), @(3), @(4)];
   
    [array enumerateObjectsUsingBlock:^( NSNumber *num, NSUInteger idx, BOOL *stop) {
        NSLog( @"%@", num);
       
        // idx == 2 时,退出循环
        if (idx == 2) {
            *stop = YES;
        }
    }];
   
    NSLog( @"------");
   
    int index = 0;
    for ( NSNumber *num in array) {
        NSLog( @"%@", num);
       
        // 第二项,退出循环
        index++;
        if (index == 2) {
            break;
        }
    }
}


/** 外部变量访问 3 ,常见面试题 */
void blockDemo4()
{
    // 指针包含指针的地址,和指向的地址
    NSMutableString *str = [ NSMutableString stringWithString: @"hello"];
    NSLog( @"%p %p", &str, str);
   
    void(^myBlock)() = ^ {
        // 修改指针 指向内存空间里 的内容
        [str setString: @"world"];
        NSLog( @"%p %p", &str, str);
       
        NSLog( @"%@", str);
    };
   
    myBlock();
}


/** 外部变量访问 2 ,常见面试题 */
void blockDemo3()
{
    // 如果要在 block 中修改外部变量的值,需要使用 __block
    // __block 还可以在 block 被执行时, ** 同步外部变量最新的数值 ****
    // 提示:为了保证代码的可读性,通常不建议使用 __block
    __block int x = 10;
    NSLog( @"%p", &x);
   
    // 在定义 block 时,如果 block 中使用到外部的 局部 变量, block 会建立该变量的 ** 副本 **( 会记录当前 x 的数值 )
    void(^myBlock)() = ^ {
        // 在使用外部变量时,默认不允许在块代码中直接修改外部变量的数值
//        x = 80;
       
        NSLog( @"%d", x);
       
        NSLog( @"=== %p", &x);
    };
   
    x = 20;
   
    myBlock();
   
    NSLog( @"%d", x);
}


/** 外部变量访问 1 ,常见面试题 */
void blockDemo2()
{
    int x = 10;
    NSLog( @"%p", &x);
   
    // 在定义 block 时,如果 block 中使用到外部的 局部 变量, block 会建立该变量的副本 ( 会记录当前 x 的数值 )
    void(^myBlock)() = ^ {
        // 在使用外部变量时,默认不允许在块代码中直接修改外部变量的数值
//        x = 80;
       
        NSLog( @"%d", x);
       
        NSLog( @"%p", &x);
    };
   
//    x = 20;
   
    myBlock();
   
    NSLog( @"%d", x);
}


/** block 的定义 */
void blockDemo1()
{
    // block 定义,是准备好一段代码片段,在需要的时候执行
    // block C 语言的格式
    //
    // 输入: inlineblock
    // 格式:返回类型 (^blockName) ( 参数类型 ) ^( 形参列表 ) { 代码实现 };
    // 提示:如果没有参数,格式可以简化
    // 格式:返回类型 (^blockName) () ^ { 代码实现 };
   
    void (^myBlock)() = ^ {
        NSLog( @"hello block");
    };
   
    // 调用 block
    myBlock();
   
    void (^sumBlock)( int, int) = ^( int x, int y) {
        NSLog( @"%d", x + y);
    };
   
    sumBlock( 10, 20);
}



#warning 如果 block(block只要是copy的就会在堆中) 在堆中 , block 引用外界的对象 , 会导致循环引用


//    __weak IWCommonArrowItem *weakClearCache = clearCache;
//    __weak IWGeneralViewController *weakSelf = self;
   
    // typeof 可以获取括号中传递的值的真实类型
//    typeof(10) a = 10; == int a = 10;
   
//    __weak typeof(clearCache) weakClearCache = clearCache;
//    __weak typeof(self) weakSelf = self;
   
    // 如果是用__weak 修饰, 当对象销毁之后会自动将变量的值设置为nil
    // 而如果__unsafe_unretained 不会
    __unsafe_unretained typeof(clearCache) weakClearCache = clearCache;
    __unsafe_unretained typeof( self) weakSelf = self;
/**
  如何在开发中,避免 block 出现循环引用呢
 
 1>
每次碰到 self 的时候,要小心,需要思考一下
 2>
充分利用 dealloc 方法
 3
 
解决方法:
 __weak CZViewController *weakSelf = self;
 
 */


  // 为了保证代码的安全性,苹果在设计 block 的时候
   
// block 引用外部变量时,默认会做 copy 操作, copy 本身也会做一次强引用
   
// 在现在的代码中,如果 block 中有对 self 的强引用,出现循环引用的情况
   
   
// 解决方法,在 oc 中,对象默认都是强引用, __weak 可以变成弱引用


OC语言BLOCK和协议
一、BOLCK
(一)简介

BLOCK是什么?苹果推荐的类型,效率高,在运行中保存代码。用来封装和保存代码,有点像函数,BLOCK可以在任何时候执行。
BOLCK和函数的相似性:(1)可以保存代码(2)有返回值(3)有形参(4)调用方式一样。
标识符 ^


(二)基本使用
(1)定义BLOCK变量
Int (^SumBlock)(int,int);//有参数,返回值类型为int
Void (^MyBlock)()//无参数,返回值类型为空
(2)利用block封装代码

  
(3)Block访问外部变量
1)Block内部可以访问外部变量;
2)默认情况下,Block内部不能修改外部的局部变量
3)给局部变量加上__block关键字,则这个局部变量可以在block内部进行修改。
 
(4)利用typedef定义block类型(和指向函数的指针很像)
Typedef int(^MyBlock)(int ,int);
以后就可以利用这种类型来定义block变量了。
MyBlock a,b;  
a=^(int a,int b){return a-b;};
MyBlock b2=^(int n1,int n2){return n1*n2;};
 
二、Protocol(协议)
(一)简介

1.Protocol:就一个用途,用来声明一大堆的方法(不能声明成员变量),不能写实现。
2.只要某个类遵守了这个协议,就拥有了这个协议中的所有方法声明。
3.只要父类遵守了某个协议,那么子类也遵守。
4.Protocol声明的方法可以让任何类去实现,protocol就是协议。
5.OC不能继承多个类(单继承)但是能够遵守多个协议。继承(:),遵守协议(< >
6.基协议:<NSObject>是基协议,是最根本最基本的协议,其中声明了很多最基本的方法。
7.协议可以遵守协议,一个协议遵守了另一个协议,就可以拥有另一份协议中的方法声明。
 
(二)基本使用

创建一个协议

遵守协议

完成协议中声明的方法的实现

测试程序

1.协议的定义
@protocol 协议名称 <NSObject>
//方法声明列表
@end;
2.如何遵守协议
(1)类遵守协议
@protocol 类名:父类名 <协议名称1,协议名称2>
@end
(2)协议遵守协议
@protocol 协议名称 <其他协议名称]]]]>
@end;
3.协议方法声明中的关键字
(1)required (默认)要求实现,若没有实现则警告但不报错
(2)Optional 不要求实现
4.定义变量时遵守协议的限制
类名<协议名称> *变量名    NSObject<.Myprotocol> *obj;
Id  <协议名称>  变量名   id  <.Myprotocol> obj1;
 
5.Property中声明的属性也可以做遵守协议的限制
@property (nonatomic ,strong ) 类名<协议名称> *属性名;
@property (nonatomic ,strong ) id<协议名称>  属性名;
 
6.补充知识:协议本身写在.h头文件中,但也可以定义在任何地方。当这个协议只有这个类使用遵守时,一般把协议写在这个类里边,当这个协议需要多个类去实现时,就写在外边单独的文件中。



A对象是请求者    B对象是响应者

代理的使用  

  1. A制定协议(申明代理方法)

@protocol HMTourViewDelegate <NSObject>

- (void)tourViewBtnDidClick:(HMTourView *)tourView

@end

 

  1. A定义代理属性

@property(nonatomic,weak)id <协议名称> delegate;

  1. B遵守协议

@interface ViewController ()<HMTourViewDelegate>   任何对象遵守协议都可以成为代理

  1. B成为代理

tourView.delegate = self;

  1. B在代理的类实现方法

#pragma mark - 代理方法

/**

 HMTourViewDelegate

 */

- (void)tourViewBtnDidClick:(HMTourView *)tourView {

    

}

  1. A让代理帮助调用方法实现功能

        if ([self.delegate respondsToSelector:@selector(tourViewBtnDidClick:)]) {

    

            [self.delegate tourViewBtnDidClick:self];

        };

 

 

通知

  1. A制定通知名称
    • .h文件低端写通知名称

@interface HMTourView : UIView

@end

UIKIT_EXTERN NSString *const HMMuneBtnDidClickNotification;

  • .m顶端写通知的真名称  (字符串)

#import "HMTourCell.h"

NSString *const HMMuneBtnDidClickNotification = @"HM";

@implementationHMTourView

@end

 

  1. A发布通知(通知内容为字典)(多种方法)

[[NSNotificationCenter defaultCenter] postNotificationName:HMMuneBtnDidClickNotification object:self userInfo:@{@"key":str}];

  1. B注册通知(方法多种)

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(xx:) name:HMMuneBtnDidClickNotification object:mune];

  1. B完成通知方法,实现功能

#pragma mark - 通知

- (void)xx:(NSNotification *)notification {

    

//    NSLog(@"%@",notification);

    NSDictionary *dic = notification.userInfo;

    self.titleLabel.text = [NSString stringWithFormat:@"%@个按钮",dic[@"key"]];

}

  1. B销毁通知

#pragma mark - 记得消除通知

- (void)dealloc {

    

    [[NSNotificationCenter defaultCenter] removeObserver:self];

}

 

Block

  1. A定义block别名

typedef void(^MnueBlock)(NSString *str);

  1. A定义block类型属性

@property(nonatomic,copy)MnueBlock mnueBlock;

  1. B传入block代码块参数给A属性

 mune.mnueBlock = ^ (NSString *str){

      

        NSLog(@"%@",str);

        self.titleLabel.text = [NSString stringWithFormat:@"%@个按钮",str];

    };

 

  1. A调用block完成功能(block可以传入参数)
  self. mnueBlock(str);



Block(函数式编程思想)
  • Block是一个数据类型,有自己的ISA指针,可以保存代码块,多用于参数的传递,只有调用Block的时候才会执行Block中的代理块
  • 定义一个block的时候需要用copy,代码块是在栈区分配,一旦离开作用域就会被释放,所以要用copy
  • __Block关键字理解
    • 正常情况下,在Block{ }中不能对外面的变量进行更改(报错)
    • 给想要更改的变量加上__Block关键字即可
  • Block理解
    • 自身view功能不能实现时,自定义一个block请求控制器去实现
    • block传的参数是给控制器用来实现方法
  • 解决Block循环引用问题
    • 当在大括号中出现self或者下划线的属性,可能造成死循环
    • __weak typeof (self) weakSelf  =self  typeof( )会自动识别括号中的对象类型
  • 与delegate区别
    • delegate回调更多的时面向过程,block则面向结果



代理/通知
  • 代理存在的意义,面向过程思想,将业务逻辑模块化
  • 代理是一对一,在代理中,只有声明并实现了代理协议的对象才能调用
  • 通知是多对多
    • 监听器可以监听多个不同条件的通知,也可以监听多个人发布的通知,不需要知道谁发通知谁接收通知
    • 监听范围是整个应用






//1. 创建通知中心 , 一个应用程序只有一个通知中心,它是一个单例对象
     NSNotificationCenter  *center=[ NSNotificationCenter   defaultCenter ];
//2 创建通知监听器:只要有满足条件的通知发布,通知监听器就会自动监听到这个通知,从而进行处理

    /*

     1.addObserver:通知的接收者--通知监听者

     2.selector:通知接收者接收到通知之后,调用它的那一个方法进行处理.方法一定要添加一个:传递参数---系统会默认将当前通知对象做为参数传递给方法处理者

     3.name:接收的通知的名称,如果为nil,那么就可以接收到任意名称的通知

     4.object:通知的发布者,如果为nil,则可以接收到任何人发布的通知

     */

    [center addObserver:p1 selector:@selector(rent:) name:@"一房一厅" object:h1];

    

    [center addObserver:p2 selector:@selector(rent:) name:nil object:nil];

    //3.发布通知

    /*

     1.postNotificationName:通知名称

     2.object:谁发布的通知

     3.userInfo:当前通知的相关信息

     */

//    [center postNotificationName:@"一房一厅" object:h1 userInfo:@{@"floor":@"3",@"price":@"300"}];

    [center postNotificationName:@"一房一厅" object:h1userInfo:@{@"floor":@"3",@"price":@"300"}];


    [center  postNotificationName : @" 单间 "   object :h2 userInfo : @{ @"floor" : @"3" , @"price" : @"300" } ];



协议和代理对于一个新手来说确实不讨好理解,也有很多的iOS开发的老手对此是懂非懂的。网上的很多博文只是讲了怎么使用,并没有说的很明白。下面我谈一下我的理解。

1.你要先搞明白,协议和代理为什么会出现,也就是说它存在的意义是什么,解决了什么问题。

协议和代理是模块化开发和封装的产物。
先讲一个小故事帮助大家理解:

老王有一家餐馆,刚刚开始的时候规模很小,所以老王一个人做了所有的事情:扫地,做菜,迎宾,上菜,收银。但是后面随着规模的扩大,老王一个人就吃不消了,忙死也忙不过来了。这时候怎么办?大家都很清楚吧,招人呗!所以后面就有了服务员,收银员,大厨,保洁员。
这就意味着原先老王的工作按模块进行了拆分。


老王.png

餐馆的工作流程(业务逻辑)简单来说是这样的:点餐->做菜->上菜->收银->打扫卫生。

转换成编程世界的模型就是这样的:业务不是很复杂的时候,我们把所有的功能都写在一个类里面,这个类暂且叫老王,理论上所有的事情和功能都可以写到这个类里面。做菜方法,上菜方法,打扫方法......就造成了老王这个类非常的庞大和臃肿,并且容易出错。

那我们开始招人了,新建了大厨类,服务员类,收银类,保洁类,这四个类。大厨类有做菜方法,服务员类点菜,上菜方法,收银类有收银方法,保洁类有打扫方法。

仅仅这样还是不行的,因为模块开发必然就有模块分化以后模块之间的通信问题。大厨类只做菜 但是菜做好了怎么办,必须及时的上菜,让顾客享用。但是大厨自己不能上菜,所以大厨必须抛出菜做好了的信号,具体这个菜上不上,怎么上,就不是大厨关心的了。

2.协议和代理所发挥的作用

老王交代大厨,你只管做菜,菜做好了以后喊一声菜做好了(我见过一个餐馆是拉铃铛)。
那么老王跟大厨定的这个规矩就是协议(protocol),下面看代码:

DaChu.h

/**
 *  下面是声明协议的固定格式,DaChuDelegate是协议的名称,因为是代理协议,名称格式为:类名+Delegate
 */
@protocol DaChuDelegate <NSObject>
- (void)doSomethingAftercaiZuohaole;
@end

@interface DaChu : NSObject
/**
 *  delegate 是dachu类的一个属性,weak 关键字是为了避免循环引用,<DaChuDelegate>表示遵守DaChuDelegate协议
 *  更加直白点:在大厨心里有一个人接受他的菜好了的信号去做一些事情,具体这个人是谁,大厨不关心,这个人的代号是delegate
 */
@property (nonatomic, weak) id <DaChuDelegate> delegate;
- (void)kaiShiZuoCai;
@end
Dachu.m
#import "DaChu.h"

@implementation DaChu
- (void)kaiShiZuoCai{
    NSLog(@"开始做菜");
    sleep(2);
    NSLog(@"做好菜了,该上菜了");

   //下面这句是判断 一下delegate是否实现了doSomethingAftercaiZuohaole方法,如果delegate没有实现
    //直接[self.delegate doSomethingAftercaiZuohaole];会crash
    if ([self.delegate respondsToSelector:@selector(doSomethingAftercaiZuohaole)]) {
        [self.delegate doSomethingAftercaiZuohaole];
    }

}
@end

下面看一看laowang这个类里面的内容

#import "LaoWang.h"
#import "DaChu.h"

@interface LaoWang ()<DaChuDelegate>//<DaChuDelegate>表示遵守DaChuDelegate协议,并且实现协议里面的方法

@end

@implementation LaoWang
- (void)laoWangKaiYe{
    NSLog(@"老王开业了");

    DaChu *dachu1 = [[DaChu alloc] init];
    dachu1.delegate = self;//说明老王充当代理的角色,负责接收菜好了的信号。
    [dachu1 kaiShiZuoCai];//大厨开始做菜
}
- (void)doSomethingAftercaiZuohaole{
    NSLog(@"老王知道了");//这里可以通知服务员去上菜了
}
@end

上面的这个过程,也可以看下图来理解:



Grand Central Dispatch,或者简称 GCD,是一个与 Block Object 产生工作的低级的 C API。GCD 真正的用途是将任务分配到多个核心又不让程序员担心哪个内核执行哪个任务。 在 Max OS X 上,多内核设备,包括笔记本,用户已经使用了相当长的时间。通过多核设备 比如 iPad2 的介绍,程序员能为 iOS 写出神奇的多核多线程 APP。

GCD 的核心是分派队列。不论在 iOS 还是 Max OS X 分派队列,正如我们快看到的是 由位于主操作系统的 GCD 来管理的线程池。你不会直接与线程有工作关系。你只在分派队 列上工作,将任务分派到这个队列上并要求队列来调用你的任务。GCD 为运行任务提供了 几个选择:同步执行、异步执行和延迟执行等。

GCD+Block 可以是一对好兄弟啊!配合起来使用真的太犀利!
GCD和Block都是苹果后来推崇的方式。GCD,自我感觉主要封装了多线程编程的复杂性!而且,使用方便。
Block 主要就是语法类似C的一种块语言。对某一个功能控制有了更好的封装,比如可以调用对象内变量,无需再传参;可以在实现方法中直接写实现后的代码,而不用再进行委托跳转,使代码更加简洁。等等,好处多多吧,自己体会!
那就开始吧!
先看一个例子:

 

// 主线程队列,由系统自动创建并且与应用撑血的主线程相关联。


     dispatch_queue_t  mainQueue = dispatch_get_main_queue ();


    

    // 要在主线程队列中,执行的Block

    dispatch_async(mainQueue, ^(void) {

        [[[UIAlertView alloc] initWithTitle:@"GCD"

                                    message:@"GCD is amazing!"

                                   delegate:nil cancelButtonTitle:@"OK"

                          otherButtonTitles:nilnilshow];

    });

dispatch_get_main_queue()
用来得到主队列。主队列又系统自动创建并且与应用程序主线程相关联。
也就是我们常说的能调用主线程修改UI的队列,即:主线程队列。

dispatch_async 方法

 void dispatch_async(dispatch_queue_t queue,dispatch_block_t block);


queue:队列
block:代码Block

根据指定的队列执行相应的Block。

dispatch_async_f 方法

void dispatch_async_f(dispatch_queue_t queue,void *context,dispatch_function_t work);

queue:指定执行该work的队列


void *context:所使用的 application-defined应用程序范围内有效的,也就是全局的)级别的参数。这是个C语法,void * 是一个无类型指针。也就是说,用它可以指向任何内存数据。


work:在指定队列(queue 参数)中要执行的方法。在该方法中,第一个参数所指代的数据,也就是dispatch_async_f方法所使用的第二个参数(void *context)所指带的数据。

例子:

// 定义结构体

typedef struct{

    char *title;

    char *message;

    char *cancelButtonTitle;

} AlertViewData;



// 定义dispatch_function_t 所执行的方法

void displayAlertView(void *paramContext){

    AlertViewData *alertData = (AlertViewData *)paramContext;

    

    NSString *title =[NSString stringWithUTF8String:alertData->title];

    NSString *message =[NSString stringWithUTF8String:alertData->message];

    NSString *cancelButtonTitle =[NSString stringWithUTF8String:alertData->cancelButtonTitle];

    

    [[[UIAlertView allocinitWithTitle:title message:message

                               delegate:nil

                      cancelButtonTitle:cancelButtonTitle otherButtonTitles:nilnilshow];

    

    

    // 释放结构体所占用的内存

    free(alertData);

    

}



// **执行dispatch_async_f

    

    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    

    // 为结构体分配内存

    AlertViewData *context = (AlertViewData *) malloc(sizeof(AlertViewData));

    

    // 初始化结构体

    if (context != NULL){

        context->title = "GCD";

        context->message = "GCD is amazing.";

        context->cancelButtonTitle = "OK";

        

        // GCD执行异步方法:指定主队列(mainQueue),传递结构体数据(context)来执行displayAlertView方法。

        dispatch_async_f(mainQueue,(void *)context, displayAlertView);

    }

有了上面的说明铺垫,我想很快就能明白其中的含义了!

dispatch_async 方法

 void dispatch_async(dispatch_queue_t queue,dispatch_block_t block);

在指定的队列上,执行响应的Block。
例子:

dispatch_queue_t mainQueue1 = dispatch_get_main_queue();

    dispatch_async(mainQueue1, ^(void) {

        NSLog(@"Current thread = %@", [NSThread currentThread]);

        NSLog(@"Main thread = %@", [NSThread mainThread]);

    });



输出:

2013-03-27 15:45:54.501 DemoVideo[21802:707] Current thread = {name = (null), num = 1}

2013-03-27 15:45:54.503 DemoVideo[21802:707] Main thread = {name = (null), num = 1}


可见,dispatch_get_main_queue() 队列所执行的Block就是在主线程上执行的。

用GCD 同步执行Non-UI-Related 任务
同步:

例如,你想下载一个图片并想在下载完成之后展现给用 户。下载过程却和 UI 没有任何关系。对于任何与 UI 无关的任务,你可以使用 GCD 中的全 局并发队列。它们允许同步和异步执行。如果你同步提交一个任务到一个并发队列,同时提交另一个同步任务到另一个并发队列;相对而言这两个同步任务将异步运行,因为他们运行在两个不同的并发队 列上。你想确定在 B 任务开始之前 A 任务完 成了。那么必须把它们同时提交一个相同的队列。

dispatch_get_global_queue 方法

dispatch_queue_t dispatch_get_global_queue(long priority,unsigned long flags);


priority:优先级


flags:暂时还没有用到,为0.


获得全局队列,根据优先级。
优先级参数取值:

#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

dispatch_sync 方法

void dispatch_sync(dispatch_queue_t queue,dispatch_block_t block);

queue:指定的队列

block:执行的代码Block


在指定的队列中同步执行Block。同步是指,当循环执行某个Block时,当第一个Block执行完成以后,再执行下一个Block。
例子:

// 得到默认优先级队列

    dispatch_queue_t concurrentQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_sync(concurrentQueue, printFrom1To1000);// 同步执行

    dispatch_sync(concurrentQueue, printFrom1To1000);// 同步执行



// 同步执行的Block

void (^printFrom1To1000)(void) = ^{

    NSUInteger counter = 0;

    for (counter = 1;counter <= 1000;counter++){

        NSLog(@"Counter = %lu - Thread = %@",(unsigned long)counter, [NSThreadcurrentThread]);

    }

};

用GCD 异步执行Non-UI-Related 任务
异步:

在主队列、串行队列和并发队列上异步执行代码块才能见识到 GCD 的真正实力。你将会完全相信 GCD 是多线程应用的未来,并将完全取代 现代应用中的线程。 

我们换一种方式讲解异步。前面代码也稍微提了一些关于异步。
一般我们异步执行,需要用的两个方法为:
dispatch_async 方法
dispatch_async_f 方法
我们来一个综合的例子:

- (void) viewDidAppear:(BOOL)paramAnimated{

    dispatch_queue_t concurrentQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(concurrentQueue, ^{

        __block UIImage *image = nil;

        dispatch_sync(concurrentQueue, ^{

            // 同步下载图片

            NSString *urlAsString =@"http://images.apple.com/mobileme/features/images/ipad_findyouripad_20100518.jpg";

            NSURL *url = [NSURL URLWithString:urlAsString];

            NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];

            NSError *downloadError = nil;

            NSData *imageData = [NSURLConnectionsendSynchronousRequest:urlRequest

                                                      returningResponse:nil

                                                                 error:&downloadError];

            if (downloadError == nil && imageData != nil){

                image = [UIImage imageWithData:imageData]; 

            }else if (downloadError != nil){

                NSLog(@"Error happened = %@", downloadError);

            } else {

                NSLog(@"No data could get downloaded from the URL."); }

        });

        

        dispatch_sync(dispatch_get_main_queue(), ^{

            // 直到下载图片完成,再调用主线程,更新UI

            if (image != nil){

                UIImageView *imageView = [[UIImageView allocinitWithFrame:self.view.bounds];

                [imageView setImage:image];

                [imageView setContentMode:UIViewContentModeScaleAspectFit];

                [self.view addSubview:imageView];

            } else {

                NSLog(@"Image isn't downloaded. Nothing to display.");

            }

        });

    });

}

当页面显示时,用异步方式去下载图片,下载图片过程将同步执行,先下载图片数据,然后再调用主线程队列来更新UI。
用GCD延迟执行任务
如果,我们需要某个方法在某一段时间后,执行,那么我们常常会调用这样的方法:

- (void)viewDidLoad{

    [super viewDidLoad];

    

    [self performSelector:@selector(printString:)

               withObject:@"Grand Central Dispatch"

               afterDelay:3.0];

    

}


- (void) printString:(NSString *)paramString{

    NSLog(@"%@", paramString);

}

但是,我们要讲的时GCD方式。那么再GCD的世界里,也有类似的2个方法,来实现延迟执行的功能。
dispatch_after 方法

void dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block);

when:所指定的时间


queue:指定的队列


block:执行的Block

将Block加入指定队列,并按照指定时间执行。
先上例子:

- (void)viewDidLoad{

    [super viewDidLoad];

    

    double delayInSeconds = 2.0;

    

    // 创建延期的时间 2S,因为dispatch_time使用的时间是纳秒,尼玛,比毫秒还小,太夸张了!!!

    dispatch_time_t delayInNanoSeconds =dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);

    // 得到全局队列

    dispatch_queue_t concurrentQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 延期执行

    dispatch_after(delayInNanoSeconds, concurrentQueue, ^(void){

        NSLog(@"Output GCD !");

        

    });

    

}

dispatch_time 方法

dispatch_time_t dispatch_time(dispatch_time_t when,int64_t delta);


when:指定的开始点,可以用 DISPATCH_TIME_NOW 来指定一个当前的时间点


delta纳秒数 

创建一个时间点( dispatch_time_t),返回的时间点为: delta+ when
dispatch_time_t:纯粹就是一个时间点。

dispatch_after 方法


void dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block);


when:时间点


queue:指定的队列


block:执行的Block

将Block加入到指定的队列,并且在指定的时间点执行。

dispatch_after_f 方法

我想有了,前面知识的铺垫,这个方法理解起来,就像在切菜!!!
直接上例子:

- (void)viewDidLoad{

    [super viewDidLoad];

    

    double delayInSeconds = 2.0;

    dispatch_time_t delayInNanoSeconds =dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);

    dispatch_queue_t concurrentQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    

    dispatch_after_f(delayInNanoSeconds,concurrentQueue, @"GCD"processSomething);


}


void processSomething(void *paramContext){

    

    NSLog(@"This is %@",paramContext);

}

注意: dispatch_after_f 执行的是一个纯C函数( processSomething)!
使用GCD来实现单例模式

在 APP 的生命周期内你想确保每段代码只执行一次,即使它在代码的不同地方多次调用(比如单例的初始化)。 
直接代码:

static dispatch_once_t onceToken;

void (^executedOnlyOnce)(void) = ^{

    static NSUInteger numberOfEntries = 0;

    numberOfEntries++;

    NSLog(@"Executed %lu time(s)", (unsigned long)numberOfEntries);

};

调用:

dispatch_queue_t concurrentQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    

    dispatch_once(&onceToken, ^{

        dispatch_async(concurrentQueue,executedOnlyOnce);

    });


    

    dispatch_once(&onceToken, ^{

        dispatch_async(concurrentQueue,executedOnlyOnce);

    });

可以发现,输出只有一行,虽然我们调用了2次。因为我们传入的 dispatch_once_t是相同的。编译器只执行一次操作。

dispatch_once 方法

void dispatch_once(dispatch_once_t *predicate,dispatch_block_t block);


predicate:单例标识符

block:执行的Block 

在Application周期内,只执行一次Block。即:单例模式
dispatch_once_t:可以理解为单例标识符, dispatch_once方法用它来测试Block是否被执行过了。如果执行过了,那么就不在执行。
用 GCD 将任务分组     
有时候,我们可能执行一系列的任务。 由于彼此之间的依赖关系。比如有3个任务:A、B、C;我们必须执行了任务A,才能执行 任务B,最后执行任务C。这样的话,我们可以用GCD的分组机制来将多个任务来按照预定的顺序来执行。


先看例子吧:



我们先来搞清楚以下的关于调度组的一些常用方法

dispatch_group_t 方法


typedef struct dispatch_group_s *dispatch_group_t;


 

指代一个调度组。
调度组负责监视加入到该组的多个Block。每个Block可以是异步或者同步(取决与你自己)。
调度组中的Block可以在不同的队列中执行;并且一个Block可以被添加到多个调度组中。

dispatch_group_create 方法


dispatch_group_t dispatch_group_create(void);


创建一个调度组


dispatch_group_async 方法


void dispatch_group_async(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);


group:要分发到的调度组


queue:执行Block的队列


block:要执行的Block


将Block分发到指定的队列(用指定的队列来执行Block),并且将该Block加入到指定的调度组中。


dispatch_group_notify 方法


void dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);


当调度组中的所有Block被执行完成后,将调用被分配到指定队列的Block。


dispatch_release 方法


void dispatch_release(dispatch_object_t object);


object:调度对象


销毁指定的调度组。

理论一大堆了,还是用事实来说话:

例子:

- (void)viewDidAppear:(BOOL)animated{

    

    [super viewDidAppear:animated];

    

    dispatch_group_t taskGroup = dispatch_group_create();// 创建一个调度组

    dispatch_queue_t mainQueue = dispatch_get_main_queue();// 创建队列

    

    // 任务1

    // Block添加到指定的调度组(taskGroup)中,并且该Block用指定的队列(mainQueue)执行。

    dispatch_group_async(taskGroup, mainQueue, ^{

        

        [self reloadTableView];

    });

    

    // 任务2

    // Block添加到指定的调度组(taskGroup)中,并且该Block用指定的队列(mainQueue)执行。

    dispatch_group_async(taskGroup, mainQueue, ^{

        

        [self reloadScrollView];

    });

    

    // 任务3

    // Block添加到指定的调度组(taskGroup)中,并且该Block用指定的队列(mainQueue)执行。

    dispatch_group_async(taskGroup, mainQueue, ^{

        

        [self reloadImageView];

    });

    

    // 当指定调度组(taskGroup)中的所有Block都执行完成后,将执行给定的Block,用指定的队列(mainQueue)。

    dispatch_group_notify(taskGroup, mainQueue, ^{

        // 指定的Block

        [[[UIAlertView alloc] initWithTitle:@"Finished"

                                    message:@"All tasks are finished"delegate:nil

                          cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]show];

        

    });

    

    // 最后,必须release 掉调度组(taskGroup

    dispatch_release(taskGroup);


}  

#pragma mark - 执行的多个方法


- (void) reloadTableView{

    

    NSLog(@"%s"__FUNCTION__);

}


- (void) reloadScrollView{


    NSLog(@"%s"__FUNCTION__);

}


- (void) reloadImageView{


    NSLog(@"%s"__FUNCTION__);

    

}

执行结果:

2013-03-28 23:00:57.394 DemoVideo[25096:707] -[MoreViewController reloadTableView]

2013-03-28 23:00:57.395 DemoVideo[25096:707] -[MoreViewController reloadScrollView]

2013-03-28 23:00:57.396 DemoVideo[25096:707] -[MoreViewController reloadImageView]

可见,执行顺序,跟加入到调度组中的次序是一样的。
等等,有点疑惑是不是?!
我对每个任务用的是方法 dispatch_group_async,是异步的啊。为什么顺序却不变呢?!
呵呵,基础不扎实啊!因为他们都是分配给同一个队列 dispatch_get_main_queue() 中的!在一个队列中是串行的啊。所以,还是按照顺序来。
对,在GCD中,每个方法往往都有两种,一种是执行标准Block,一种是执行C函数的。
那么,就ok,我们来看看执行C函数的写法:
例子:

- (void)viewDidAppear:(BOOL)animated{

    

    [super viewDidAppear:animated];

    

    dispatch_group_t taskGroup = dispatch_group_create();

    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    dispatch_group_async_f(taskGroup, mainQueue,(void *)selfreloadAllComponents);

    

    dispatch_group_notify(taskGroup, mainQueue, ^{

        

        [[[UIAlertView alloc] initWithTitle:@"Finished"

                                    message:@"All Tasks are Finished"

                                   delegate:nil

                          cancelButtonTitle:@"OK" otherButtonTitles:nilnil] show];

        });

    

    dispatch_release(taskGroup);

        


}

// 定义调度组要执行的C函数

void reloadAllComponents(void *context){

    MoreViewController *self =(MoreViewController *)context;

    [self reloadTableView];

    [self reloadScrollView];

    [self reloadImageView];

}

注意:
因为 dispatch_group_async_f不像 dispatch_group_t那样使用Block,可以访问当前类的变量。
由于  dispatch_group_async_f  接受  函数作为一个代码块来执行,所以,我们要执行 reloadTableView方法, reloadScrollView 方法reloadImageView方法,必须有个当前类的引用!!!!!
那么  C 函数必须有一 个引用到  Self,这个 Self 能够调用当前对象的实例方法 
用 GCD 创建自己的分发队列     
当然,GCD也给予了我们很多自主性质的操作。就是可以定义我们自己的分发队列。
有时候,突发奇想,自己定义一个队列,做自己的事情!那该多自由!这一定可以有的,哟,亲!!
爽啊!!!
注意:我们自定义的队列,往往不会在主队列中执行。而是单独分配一个线程来维护我们所定义的队列。
先来看代码吧:

- (void)viewDidAppear:(BOOL)animated{

    

    [super viewDidAppear:animated];

    

    // 创建指定的自定义的串行队列

    dispatch_queue_t firstSerialQueue =dispatch_queue_create("com.pixolity.GCD.serialQueue1", NULL);

    

    // 让队列异步执行Block

    dispatch_async(firstSerialQueue, ^{

        NSUInteger counter = 0;

        for (counter = 0; counter < 5; counter++){

            NSLog(@"First iteration, counter = %lu", (unsigned long)counter); }

            NSLog(@"Current thread = %@", [NSThread currentThread]);

    });

    

    dispatch_async(firstSerialQueue, ^{

        NSUInteger counter = 0for (counter = 0;counter < 5;counter++){

            NSLog(@"Second iteration, counter = %lu", (unsigned long)counter);

            NSLog(@"Current thread = %@", [NSThread currentThread]);

        }

    });

    

    dispatch_async(firstSerialQueue, ^{ NSUInteger counter = 0;

        for (counter = 0;counter < 5;counter++){

            NSLog(@"Third iteration, counter = %lu", (unsigned long)counter);

            NSLog(@"Current thread = %@", [NSThread currentThread]);

        }

    });

    

    // 销毁队列

    dispatch_release(firstSerialQueue);

    

    // 输出主队列,比较会发现,我们自定义的队列,并不在主线程上,效率还是蛮高的。

    dispatch_queue_t mainQueue1 = dispatch_get_main_queue();

    dispatch_async(mainQueue1, ^(void) {

        NSLog(@"Main thread = %@", [NSThread mainThread]);

    });

        


}

代码,不解释!
对,上面是Block形式的。还有一个C函数的自定义吧!
贴,贴更健康!

// 定义任务1-C函数形式

void firstIteration(void *paramContext){

    NSUInteger counter = 0;

    for (counter = 0;counter < 5;counter++){

        NSLog(@"First iteration, counter = %lu", (unsigned long)counter);

    }

}


// 定义任务2-C函数形式

void secondIteration(void *paramContext){

    NSUInteger counter = 0;

    for (counter = 0;counter < 5;counter++){

        NSLog(@"Second iteration, counter = %lu", (unsigned long)counter);

    }

}


// 定义任务3-C函数形式

void thirdIteration(void *paramContext){

    NSUInteger counter = 0;

    for (counter = 0;counter < 5;counter++){

        NSLog(@"Third iteration, counter = %lu", (unsigned long)counter);

    }

}

- (void)viewDidAppear:(BOOL)animated{

    

    [super viewDidAppear:animated];

    

    dispatch_queue_t firstSerialQueue =dispatch_queue_create("com.pixolity.GCD.serialQueue1", 0);

    dispatch_async_f(firstSerialQueue, NULLfirstIteration);

    dispatch_async_f(firstSerialQueue, NULLsecondIteration);

    dispatch_async_f(firstSerialQueue, NULLthirdIteration);

    dispatch_release(firstSerialQueue);

        


}

用 GCD   进行信号量的管理操作

dispatch_semaphore_create 方法

dispatch_semaphore_t dispatch_semaphore_create(long value);

创建一个信号量对象


value:信号量的值,必须大于等于0


什么是信号量?


我想举个例子你就会明白了。古时候执行某种命令需要令牌。创建一个信号量,就是创建一个命令。

dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);

也就是说,一项任务semaphore,有3个令牌,就是说,最多可以同时分派3名不同的将领去执行。



dispatch_semaphore_wait

Waits for (decrements) a semaphore.

long dispatch_semaphore_wait(dispatch_semaphore_t dsema,dispatch_time_t timeout);

递减信号量。

dsema:信号量


timeout:等待信号量的策略

返回0,说明递减信号量成功。

也就是说,拿走一个令牌去执行某个任务。这时候,令牌数量会减去1。当令牌用完时,使用FIFO的原则进行等待。


dispatch_semaphore_signal


long dispatch_semaphore_signal(dispatch_semaphore_t dsema);

增加信号量

也就是说,某一个将领执行任务回来。并且将令牌上交。这样处于等待中的将领可以得到令牌并执行相应任务

例子:
使用信号量实现锁操作。

//主线程中

    TestObj *obj = [[TestObj allocinit];

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

    

    //线程1

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        long thecount= dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

        NSLog(@"%ld",thecount);

        [obj method1];

        sleep(10);

        dispatch_semaphore_signal(semaphore);

    });

    

    //线程2

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        sleep(1);

        long thecount= dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

        NSLog(@"----%ld",thecount);


        [obj method2];

        dispatch_semaphore_signal(semaphore);


    });











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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值