GCD

来源:互联网 发布:大阪 香港知乎 编辑:程序博客网 时间:2024/05/16 19:53


{

    1. GCD 中重点! 和线程有关!队列 执行函数


    2.知识点/小技巧延时执行("运行循环!") /队列组 /单例(一次性代码) /阻塞式函数

}


GCD : 队列 / 执行函数!



队列: 存放任务"FIFO" 先进先出原则


串行队列:用来"存放"想要"按顺序"执行的任务.需要自己手动创建!


并发队列:用来"存放"想要"同时"执行的任务.需要自己手动创建! "阻塞式函数可以阻塞并发队列"


两个特殊队列:不需要手动创建/系统已经自动生成了!


全局并发队列:用来"存放"想要"同时"执行的任务.不需要自己手动创建!/"阻塞式函数不能阻塞全局并发队列"!


主队列:用来存放在"主线程"中执行的任务! -- 主队列中的任务一定要交给主线程来执行!



执行函数:


"同步"执行函数:有顺序!安全!"不具备"开启新线程的能力,只能在当前线程执行任务!


"异步"执行函数:同时执行/不安全!"具备"开启新线程的能力,是否开启线程还和队列有关!



/*------------------------------ GCD使用 1.队列和任务------------------------------------------*/

重点:1."串行队列"?"并发队列"?2.block?

{

    1.GCD(Grand Central Dispatch) ---- '牛逼的中枢调度器'!

    // C语言框架 /自动管理线程的生命周期(创建/释放)

    

    推出GCD的目的:取代NSThread!

    

    "多核""并行"运算提出的解决方案!

    

    优点:

    <1> GCD 能够自动利用更多的CPU的核数(双核/四核)!

    <2> GCD 会自动管理线程的生命周期.

    

    程序员只需要告诉 GCD想要执行的任务(代码)!

    

    2.GCD中的两个核心概念:

    

    "任务":

        想要做的事情/执行什么操作.

        GCD 中的任务定义在block.

        void (^myBlock)() = ^{

            // 想要做的事情/任务

        }

    

    "队列":

        用来'存放'任务!

    

    队列 != 线程!

    队列中存放的任务最后都要由线程来执行!

    

    队列的原则:先进先出,后进后出(FIFO/ First In First Out)!


    队列的类型:

    <1> '串行'队列:(Serial Dispatch Queue)

    存放按顺序执行的任务!(一个任务执行完毕,再执行下一个任务)!

    

    // 创建一个串行队列

    dispatch_queue_t serialQueue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);

    

    <2> '并发'队列:(Concurrent Dispatch Queue)

    存放想要同时(并发)执行的任务!

    

    // 创建一个并发队列

    dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrent",DISPATCH_QUEUE_CONCURRENT);

    

    注意两个非常常用的特殊队列:

    

    <1>主队列:// UI 操作放在主队列中执行!

        跟主线程相关联的队列!

        主队列是 GCD自带的一种特殊的串行队列!

        主队列中的任务都会在主线程中执行!

    //获取主队列

    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    

    <2>全局并发队列:// 一般情况下,并发任务都可以放在全局并发队列中!

    //获取全局并发队列

    dispatch_queue_t globalQueue = dispatch_get_global_queue(0,0);

}

/*-----------------------------------  GCD使用 2.执行任务 -------------------------------------*/

重点:1."同步"函数!"异步"函数!2.容易混淆的四个概念:'串行' ,'并发' ,"同步" ,"异步"之间的区别?

{

    问题:串行队列中的任务必定按顺序执行吗?并发队列中的任务必定同时执行吗?

    

    GCD中有两个用来执行任务的函数:

    

    '同步'执行任务:

    dispatch_sync(<#dispatch_queue_t queue#>, <#^(void)block#>)

    

    '异步'执行任务:

    dispatch_async(dispatch_queue_t queue, <#^(void)block#>)


    // <#dispatch_queue_t queue#> :队列

    // <#^(void)block#>:任务

    

    "同步""异步"的区别:

    "同步":只能在'当前'线程中执行任务,不具备开启新线程的能力.

    "异步":可以在''的线程中执行任务,具备开启新线程的能力.

    

    GCD 使用有两个步骤:

    <1> 将任务添加到队列中;

    <2>选择同步还是异步的方式执行任务.

    

    注意:四个容易混淆的术语:

    '串行' ,'并发' ,"同步" ,"异步".

}

/*-------------------------------  GCD使用 3.各种队列的执行效果 ---------------------------------*/

重点:1.掌握两个常用的组合!

{

    常见的组合(掌握)

    1> dispatch_async + 全局并发队列 (可以开启多条线程)

    2> dispatch_async + 自己创建的串行队列 (开启一条线程)

    

    只有'异步'执行"并发"队列,才可以开启多条线程.

    

    注意:

    在主线程中同步执行主队列中的任务,会造成'主线程''主队列'相互等待,卡住主线程!

}

/*-------------------------------  GCD使用 4.线程间通信 ---------------------------------------*/

重点:1.从子线程回到主线程(经典用法)!2.两个注意点.

{

    1.经典用法(子线程下载(耗时操作),主线程刷新UI):

    dispatch_async(dispatch_get_global_queue(0,0), ^{

        

                       // 执行耗时的异步操作...

        

                       dispatch_async(dispatch_get_main_queue(), ^{

                           

                           // 回到主线程,执行UI刷新操作

                           

                       });

                   });


    2.注意:

    <1>需要设置按钮的image,建议先把按钮类型改为custom,才能保证设置成功

    <2> 属性名不能以new开头

}

/*-------------------------------  GCD使用 5.延时执行  -----------------------------------------*/

重点:1.iOS常见的两种延时执行方式

{

    iOS中的延时执行方式:

    // 定制好延时任务后,不会阻塞当前线程.

    

    <1> 调用 NSObject 方法:

    [self performSelector:@selector(run) withObject:nil afterDelay:2.0];

    // 2秒后再调用selfrun方法

    

    <2> GCD 函数实现延时执行:

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

        // 2秒后执行这里的代码...在哪个线程执行,跟队列类型有关

    });

    

    注意:

    不要使用sleep,会阻塞当前线程.

}

/*-------------------------------  GCD使用 6.队列组   ------------------------------------------*/

重点:1.了解队列组的使用方法.

{

    项目需求:

    首先:分别异步执行两个耗时操作;

    其次:等两次耗时操作都执行完毕后,再回到主线程执行操作.

    

    使用队列组(dispatch_group_t)快速,高效的实现上述需求.

    

    dispatch_group_t group = dispatch_group_create(); // 队列组

    dispatch_queue_t queue = dispatch_get_global_queue(0,0); // 全局并发队列

    

    dispatch_group_async(group, queue, ^{         // 异步执行操作1

        // longTime1

    });

    

    dispatch_group_async(group, queue, ^{         // 异步执行操作2

        // longTime2

    });

    

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{     // 在主线程刷新数据

        // reload Data

    });

}

/*-------------------------------  GCD使用 7.一次性代码 ---------------------------------------*/

重点:1.掌握一次性代码的实现.

{

    一次性代码:

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        // 只执行一次的代码(这里面默认是线程安全的).

    });

}

/*--------------------------------------  补充: 单例设计模式 -----------------------------------*/

重点:1.掌握单例!

{

    1.单例简介:

    

    作用:

        保证程序在运行过程中,一个类只有一个实例对象.这个实例对象容易被外界访问!

        控制实例对象个数(只有一个),节约系统资源.

    

    使用场合:

        在整个应用程序中,共享一份资源(这份资源只需要创建初始化一次).

    

    举例:

        打印机/视图窗口/一些网络工具类等等

    

    // 懒汉式:用到的时候再加载.

    // 饿汉式:只要程序运行就加载. // 不需要掌握,也不要这么写!

    // 掌握懒汉式.

    

    2.单例实现:(两种方式:互斥锁(@synchronized(self))和一次性代码(dispatch_once));

    

    2.1互斥锁@synchronized(self):

    

    <1>. .m文件中保留一个全局的 static 的实例.

    

        static id _instance;

    

    <2>.重写若干方法(allocWithZone: copyWithZone:)并提供一个类方法让外界访问唯一的实例.

    

    //(1)重写 allocWithZone:方法,在这里创建唯一的实例(注意线程安全). //alloc 内部都会调用这个方法.

        +(instancetype)allocWithZone:(struct _NSZone *)zone {

            if (_instance == nil) { // 防止频繁加锁

                @synchronized(self) {

                    if (_instance == nil) { // 防止创建多次

                        _instance = [super allocWithZone:zone];

                    }

                }

            }

            return _instance;

        }

    

    //(2)重写 copyWithZone:方法.

        +(id)copyWithZone:(struct _NSZone *)zone

        {

            return _instance;

        }

    //(3)提供1个类方法让外界访问唯一的实例

        +(instancetype)shareSingleton

        {

            if (!_instance) { // 防止频繁加锁

                @synchronized(self){

                    if (!_instance) { // 防止创建多次

                        _instance = [[self alloc] init];

                    }

                }

            }

            return _instance;

        }

    

    2.2 一次性代码(dispatch_once):

    <1>. .m文件中保留一个全局的 static 的实例.

    

    static id _instance;

    

    <2>.重写若干方法(allocWithZone: copyWithZone:)并提供一个类方法让外界访问唯一的实例.

    

    //(1)重写 allocWithZone:方法,在这里创建唯一的实例(注意线程安全).

    + (id)allocWithZone:(struct _NSZone *)zone

    {

        static dispatch_once_t onceToken;

        dispatch_once(&onceToken, ^{

            _instace = [super allocWithZone:zone];

        });

        return _instace;

    }

    

    //(2)重写 copyWithZone:方法.

    +(id)copyWithZone:(struct _NSZone *)zone

    {

        return _instance;

    }

    //(3)提供1个类方法让外界访问唯一的实例

    + (instancetype)shareSingleton

    {

        static dispatch_once_t onceToken;

        dispatch_once(&onceToken, ^{

            _instace = [[self alloc] init];

        });

        return _instace;

    }


    注意: ARC MRC 中单例的实现方式略有不同. MRC下单例的实现比 ARC 多了几个内存管理的方法:

    

    MRC 中增加如下方法的实现:

    - (instancetype)retain {return self; }

    - (NSUInteger)retainCount { return 1; }

    - (oneway void)release {}

    - (instancetype)autorelease { return self; }

    

    3.判断当前环境(ARC/MRC)

    

#if __has_feature(objc_arc)

    // ARC

#else

    // MRC

#endif

    

    4.注意两个方法:

    // 面试问题:两个方法的区别?

    <1> +(void)load;

    // 当类加载到OC运行时环境(内存)中的时候,就会调用一次(一个类只会加载一次).

    // 程序一启动就会调用.

    // 程序运行过程中,只会调用1.

    <2> +(void)initialize;

    // 当第一次使用这个类的时候(比如调用了类的某个方法)才会调用.

    // 并非程序一启动就会调用.

}




0 0
原创粉丝点击