zoukankan      html  css  js  c++  java
  • iOS开发多线程篇—GCD的常见用法

    iOS开发多线程篇—GCD的常见用法

    一、延迟执行
    1.介绍
    iOS常见的延时执行有2种方式

    (1)调用NSObject的方法

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

    // 2秒后再调用self的run方法

    (2)使用GCD函数

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

        // 2秒后异步执行这里的代码...

    });

    2.说明

    第一种方法,该方法在那个线程调用,那么run就在哪个线程执行(当前线程),通常是主线程。

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

    说明:在3秒钟之后,执行run函数

    代码示例:

    //
    //  YYViewController.m
    //  01-GCD的常见使用(延迟执行)
    //
    //  Created by apple on 14-6-25.
    //  Copyright (c) 2014年 itcase. All rights reserved.
    //
    
    #import "YYViewController.h"
    
    @interface YYViewController ()
    
    @end
    
    @implementation YYViewController
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        NSLog(@"打印线程----%@",[NSThread currentThread]);
        //延迟执行
        //第一种方法:延迟3秒钟调用run函数
        [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
        
    }
    -(void)run
    {
        NSLog(@"延迟执行----%@",[NSThread currentThread]);
    }
    
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        //在异步函数中执行
        dispatch_queue_t queue = dispatch_queue_create("wendingding", 0);
        
        dispatch_sync(queue, ^{
            [self performSelector:@selector(test) withObject:nil afterDelay:1.0];
        });
        NSLog(@"异步函数");
    }
    -(void)test
    {
        NSLog(@"异步函数中延迟执行----%@",[NSThread currentThread]);
    }
    @end

    说明:如果把该方法放在异步函数中执行,则方法不会被调用(BUG?)

     

    第二种方法,

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

           //延迟执行的方法

        });

    说明:在5秒钟之后,执行block中的代码段。

    参数说明:

          

    什么时间,执行这个队列中的这个任务。

    代码示例:

    //
    //  YYViewController.m
    //  02-GCD常见使用(延迟执行2)
    //
    //  Created by apple on 14-6-25.
    //  Copyright (c) 2014年 itcase. All rights reserved.
    //
    
    #import "YYViewController.h"
    
    @interface YYViewController ()
    
    @end
    
    @implementation YYViewController
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        NSLog(@"打印当前线程---%@",  [NSThread currentThread]);
        
        //延迟执行,第二种方式
         //可以安排其线程(1),主队列
         dispatch_queue_t queue= dispatch_get_main_queue();
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), queue, ^{
            NSLog(@"主队列--延迟执行------%@",[NSThread currentThread]);
        });
        
        //可以安排其线程(2),并发队列
        //1.获取全局并发队列
        dispatch_queue_t queue1= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        //2.计算任务执行的时间
        dispatch_time_t when=dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
        //3.会在when这个时间点,执行queue中的这个任务
        dispatch_after(when, queue1, ^{
            NSLog(@"并发队列-延迟执行------%@",[NSThread currentThread]);
        });
    }
    
    @end

    延迟执行:不需要再写方法,且它还传递了一个队列,我们可以指定并安排其线程。

    如果队列是主队列,那么就在主线程执行,如果队列是并发队列,那么会新开启一个线程,在子线程中执行。

    二、一次性代码

    1.实现一次性代码

    需求:点击控制器只有第一次点击的时候才打印。

    实现代码:

    //
    //  YYViewController.m
    //  03-GCD常见使用(一次性代码)
    //
    //  Created by apple on 14-6-25.
    //  Copyright (c) 2014年 itcase. All rights reserved.
    //
    
    #import "YYViewController.h"
    
    @interface YYViewController ()
    @property(nonatomic,assign) BOOL log;
    @end
    
    @implementation YYViewController
    
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        if (_log==NO) {
            NSLog(@"该行代码只执行一次");
            _log=YES;
        }
    }
    @end

    缺点:这是一个对象方法,如果又创建一个新的控制器,那么打印代码又会执行,因为每个新创建的控制器都有自己的布尔类型,且新创建的默认为NO,因此不能保证改行代码在整个程序中只打印一次。

    2.使用dispatch_once一次性代码

    使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

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

    });

    整个程序运行过程中,只会执行一次。

    代码示例:

    //
    //  YYViewController.m
    //  03-GCD常见使用(一次性代码)
    //
    //  Created by apple on 14-6-25.
    //  Copyright (c) 2014年 itcase. All rights reserved.
    //
    
    #import "YYViewController.h"
    
    @interface YYViewController ()
    @property(nonatomic,assign) BOOL log;
    @end
    
    @implementation YYViewController
    
    //-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    //{
    //    if (_log==NO) {
    //        NSLog(@"该行代码只执行一次");
    //        _log=YES;
    //    }
    //}
    
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSLog(@"该行代码只执行一次");
        });
    }
    @end

    效果(程序运行过程中,打印代码只会执行一次):

     

    三、队列组

    需求:从网络上下载两张图片,把两张图片合并成一张最终显示在view上。

    1.第一种方法

    代码示例:

    //
    //  YYViewController.m
    //  04-GCD基本使用(队列组下载图片)
    //
    //  Created by apple on 14-6-25.
    //  Copyright (c) 2014年 itcase. All rights reserved.
    //
    
    #import "YYViewController.h"
    //宏定义全局并发队列
    #define global_quque    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    //宏定义主队列
    #define main_queue       dispatch_get_main_queue()
    
    @interface YYViewController ()
    @property (weak, nonatomic) IBOutlet UIImageView *imageView1;
    @property (weak, nonatomic) IBOutlet UIImageView *imageView2;
    @property (weak, nonatomic) IBOutlet UIImageView *imageView3;
    
    @end
    
    @implementation YYViewController
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    }
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        //获取全局并发队列
    //    dispatch_queue_t queue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        //获取主队列
    //    dispatch_queue_t queue= dispatch_get_main_queue();
        
    //    图片1:http://d.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=2b9a12172df5e0fefa1581533d095fcd/cefc1e178a82b9019115de3d738da9773912ef00.jpg
    //    图片2:http://h.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=f47fd63ca41ea8d39e2f7c56f6635b2b/1e30e924b899a9018b8d3ab11f950a7b0308f5f9.jpg
        dispatch_async(global_quque, ^{
            //下载图片1
           UIImage *image1= [self imageWithUrl:@"http://d.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=2b9a12172df5e0fefa1581533d095fcd/cefc1e178a82b9019115de3d738da9773912ef00.jpg"];
            NSLog(@"图片1下载完成---%@",[NSThread currentThread]);
        
            //下载图片2
           UIImage *image2= [self imageWithUrl:@"http://h.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=f47fd63ca41ea8d39e2f7c56f6635b2b/1e30e924b899a9018b8d3ab11f950a7b0308f5f9.jpg"];
            NSLog(@"图片2下载完成---%@",[NSThread currentThread]);
            
            //回到主线程显示图片
            dispatch_async(main_queue, ^{
                 NSLog(@"显示图片---%@",[NSThread currentThread]);
                self.imageView1.image=image1;
                self.imageView2.image=image2;
                //合并两张图片
                UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 100), NO, 0.0);
                [image1 drawInRect:CGRectMake(0, 0, 100, 100)];
                [image2 drawInRect:CGRectMake(100, 0, 100, 100)];
                self.imageView3.image=UIGraphicsGetImageFromCurrentImageContext();
                //关闭上下文
                UIGraphicsEndImageContext();
                   NSLog(@"图片合并完成---%@",[NSThread currentThread]);
            });
            //
        });
    }
    
    //封装一个方法,传入一个url参数,返回一张网络上下载的图片
    -(UIImage *)imageWithUrl:(NSString *)urlStr
    {
        NSURL *url=[NSURL URLWithString:urlStr];
        NSData *data=[NSData dataWithContentsOfURL:url];
        UIImage *image=[UIImage imageWithData:data];
        return image;
    }
    @end

    显示效果:

     

    打印查看:

     

    问题:这种方式的效率不高,需要等到图片1.图片2都下载完成后才行。

    提示:使用队列组可以让图片1和图片2的下载任务同时进行,且当两个下载任务都完成的时候回到主线程进行显示。

    2.使用队列组解决

    步骤:

    创建一个组

    开启一个任务下载图片1

     开启一个任务下载图片2

    同时执行下载图片1下载图片2操作

    等group中的所有任务都执行完毕, 再回到主线程执行其他操作

    代码示例

    //
    //  YYViewController.m
    //  04-GCD基本使用(队列组下载图片)
    //
    //  Created by apple on 14-6-25.
    //  Copyright (c) 2014年 itcase. All rights reserved.
    //
    
    #import "YYViewController.h"
    //宏定义全局并发队列
    #define global_quque    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    //宏定义主队列
    #define main_queue       dispatch_get_main_queue()
    
    @interface YYViewController ()
    @property (weak, nonatomic) IBOutlet UIImageView *imageView1;
    @property (weak, nonatomic) IBOutlet UIImageView *imageView2;
    @property (weak, nonatomic) IBOutlet UIImageView *imageView3;
    
    @end
    
    @implementation YYViewController
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    }
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        //    图片1:http://d.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=2b9a12172df5e0fefa1581533d095fcd/cefc1e178a82b9019115de3d738da9773912ef00.jpg
        //    图片2:http://h.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=f47fd63ca41ea8d39e2f7c56f6635b2b/1e30e924b899a9018b8d3ab11f950a7b0308f5f9.jpg
        
        
        //1.创建一个队列组
            dispatch_group_t group = dispatch_group_create();
        
        //2.开启一个任务下载图片1
        __block UIImage *image1=nil;
        dispatch_group_async(group, global_quque, ^{
            image1= [self imageWithUrl:@"http://d.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=2b9a12172df5e0fefa1581533d095fcd/cefc1e178a82b9019115de3d738da9773912ef00.jpg"];
            NSLog(@"图片1下载完成---%@",[NSThread currentThread]);
        });
        
        //3.开启一个任务下载图片2
        __block UIImage *image2=nil;
        dispatch_group_async(group, global_quque, ^{
            image2= [self imageWithUrl:@"http://h.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=f47fd63ca41ea8d39e2f7c56f6635b2b/1e30e924b899a9018b8d3ab11f950a7b0308f5f9.jpg"];
            NSLog(@"图片2下载完成---%@",[NSThread currentThread]);
        });
        
        //同时执行下载图片1下载图片2操作
        
       //4.等group中的所有任务都执行完毕, 再回到主线程执行其他操作
        dispatch_group_notify(group,main_queue, ^{
            NSLog(@"显示图片---%@",[NSThread currentThread]);
            self.imageView1.image=image1;
            self.imageView2.image=image2;
            
            //合并两张图片
            //注意最后一个参数是浮点数(0.0),不要写成0。
            UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 100), NO, 0.0);
            [image1 drawInRect:CGRectMake(0, 0, 100, 100)];
            [image2 drawInRect:CGRectMake(100, 0, 100, 100)];
            self.imageView3.image=UIGraphicsGetImageFromCurrentImageContext();
            //关闭上下文
            UIGraphicsEndImageContext();
            
            NSLog(@"图片合并完成---%@",[NSThread currentThread]);
        });
        
    }
    -(void)download2image
    {
        //获取全局并发队列
    //    dispatch_queue_t queue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        //获取主队列
    //    dispatch_queue_t queue= dispatch_get_main_queue();
        
        dispatch_async(global_quque, ^{
            //下载图片1
           UIImage *image1= [self imageWithUrl:@"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"];
            NSLog(@"图片1下载完成---%@",[NSThread currentThread]);
        
            //下载图片2
           UIImage *image2= [self imageWithUrl:@"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"];
            NSLog(@"图片2下载完成---%@",[NSThread currentThread]);
            
            //回到主线程显示图片
            dispatch_async(main_queue, ^{
                 NSLog(@"显示图片---%@",[NSThread currentThread]);
                self.imageView1.image=image1;
                self.imageView2.image=image2;
                //合并两张图片
                UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 100), NO, 0.0);
                [image1 drawInRect:CGRectMake(0, 0, 100, 100)];
                [image2 drawInRect:CGRectMake(0, 0, 100, 100)];
                self.imageView3.image=UIGraphicsGetImageFromCurrentImageContext();
                //关闭上下文
                UIGraphicsEndImageContext();
                   NSLog(@"图片合并完成---%@",[NSThread currentThread]);
            });
            //
        });
    }
    
    //封装一个方法,传入一个url参数,返回一张网络上下载的图片
    -(UIImage *)imageWithUrl:(NSString *)urlStr
    {
        NSURL *url=[NSURL URLWithString:urlStr];
        NSData *data=[NSData dataWithContentsOfURL:url];
        UIImage *image=[UIImage imageWithData:data];
        return image;
    }
    @end

    打印查看(同时开启了两个子线程,分别下载图片):

     

    2.补充说明

    有这么1种需求:

    首先:分别异步执行2个耗时的操作

    其次:等2个异步操作都执行完毕后,再回到主线程执行操作

    如果想要快速高效地实现上述需求,可以考虑用队列组

    dispatch_group_t group =  dispatch_group_create();

    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // 执行1个耗时的异步操作

    });

    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // 执行1个耗时的异步操作

    });

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{

        // 等前面的异步操作都执行完毕后,回到主线程...

    });

  • 相关阅读:
    OSCP Learning Notes Buffer Overflows(3)
    OSCP Learning Notes Buffer Overflows(5)
    OSCP Learning Notes Exploit(3)
    OSCP Learning Notes Exploit(4)
    OSCP Learning Notes Exploit(1)
    OSCP Learning Notes Netcat
    OSCP Learning Notes Buffer Overflows(4)
    OSCP Learning Notes Buffer Overflows(1)
    OSCP Learning Notes Exploit(2)
    C++格式化输出 Learner
  • 原文地址:https://www.cnblogs.com/yipingios/p/5563645.html
Copyright © 2011-2022 走看看