4.4 GCD->3.0 其他用法

作者: 蓝田_Loto | 来源:发表于2016-04-18 14:20 被阅读113次

    本文并非最终版本,如果想要关注更新或更正的内容请关注文集,联系方式详见文末,如有疏忽和遗漏,欢迎指正。


    本文相关目录:
    ==================== 所属文集:4.0 多线程 ====================
    4.1 多线程基础->1.0 进程 & 线程
    ······················ 2.0 多线程简介
    4.2 pthread
    4.3 NSThread->1.0 创建线程
    ····················· 2.0 线程属性
    ····················· 3.0 线程状态/线程生命周期
    ····················· 4.0 多线程安全隐患
    ····················· 5.0 线程间通讯和常用方法
    4.4 GCD->1.0 GCD简介和使用
    ·············· 2.0 线程间的通信
    ·············· 3.0 其他用法
    ·············· 4.0 GCD 的定时器事件
    4.5 NSOperation->1.0 NSOperation简介
    ························ 2.0 NSOperationQueue
    ························ 3.0 线程间通信
    ························ 4.0 自定义NSOperation
    4.6 RunLoop - 运行循环
    ===================== 所属文集:4.0 多线程 =====================


    3.1 barrier函数

    作用:控制多线程的执行顺序

    #import "ViewController.h"
    
    @interface ViewController ()
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
      [super viewDidLoad];
      // Do any additional setup after loading the view, typically from a nib.
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
      // 这里使用全局并发队列的方式会导致 dispatch_barrier_async 功能失效
      dispatch_queue_t queue =
          dispatch_queue_create("TD", DISPATCH_QUEUE_CONCURRENT);
    
      dispatch_async(queue, ^{
        NSLog(@"----1-----%@", [NSThread currentThread]);
      });
      dispatch_async(queue, ^{
        NSLog(@"----2-----%@", [NSThread currentThread]);
      });
    
      dispatch_barrier_async(queue, ^{
        NSLog(@"----barrier-----%@", [NSThread currentThread]);
      });
    
      dispatch_async(queue, ^{
        NSLog(@"----3-----%@", [NSThread currentThread]);
      });
      dispatch_async(queue, ^{
        NSLog(@"----4-----%@", [NSThread currentThread]);
      });
    }
    
    - (void)didReceiveMemoryWarning {
      [super didReceiveMemoryWarning];
      // Dispose of any resources that can be recreated.
    }
    
    @end
    

    打印结果:

    其他用法 - barrier函数[1419:94670] ----2-----<NSThread: 0x7fc4a0c2c5d0>{number = 3, name = (null)}
    其他用法- barrier函数[1419:94732] ----1-----<NSThread: 0x7fc4a0f0cb20>{number = 2, name = (null)}
    其他用法- barrier函数[1419:94732] ----barrier-----<NSThread: 0x7fc4a0f0cb20>{number = 2, name = (null)}
    其他用法- barrier函数[1419:94732] ----3-----<NSThread: 0x7fc4a0f0cb20>{number = 2, name = (null)}
    其他用法 - barrier函数[1419:94670] ----4-----<NSThread: 0x7fc4a0c2c5d0>{number = 3, name = (null)}
    

    3.2 延迟执行

    方法1:调用NSObject的方法

    // 该方法在那个线程调用,那么run就在哪个线程执行(当前线程),通常是主线程
    // 2秒后再调用self的run方法
    [selfperformSelector:@selector(run) withObject:nilafterDelay:2.0];
    

    方法2:使用GCD函数

    // 这里是在主线程执行,如果想要在子线程执行,选择相应的队列
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            // 2秒后异步执行这里的代码...
        });
    

    方法3:使用NSTimer

    [NSTimer scheduledTimerWithTimeInterval:2.0
                                     target:self
                                   selector:@selector(run)
                                   userInfo:nil
                                    repeats:NO];
    

    代码示例:

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
      [super viewDidLoad];
      // Do any additional setup after loading the view, typically from a nib.
    }
    
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    
      [self delay1];
    }
    
    #pragma mark - 方法1:调用NSObject的方法
    - (void)delay1 {
      NSLog(@"touchesBegan-----");
    
      // 2秒后再调用self的run方法
      [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
    }
    
    #pragma mark - 方法2:使用 GCD 函数
    - (void)delay2 {
      NSLog(@"touchesBegan-----");
    
      // 这里是在主线程执行,如果想要在子线程执行,选择相应的队列
      dispatch_after(
          dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)),
          dispatch_get_main_queue(), ^{
            NSLog(@"run-----");
          });
    }
    
    #pragma mark - 方法3:使用NSTimer定时器
    - (void)delay3 {
      NSLog(@"touchesBegan-----");
    
      [NSTimer scheduledTimerWithTimeInterval:2.0
                                       target:self
                                     selector:@selector(run)
                                     userInfo:nil
                                      repeats:NO];
    }
    
    - (void)run {
      NSLog(@"run-----");
    }
    
    - (void)didReceiveMemoryWarning {
      [super didReceiveMemoryWarning];
      // Dispose of any resources that can be recreated.
    }
    
    @end
    

    打印结果:

    其他用法 - 延迟执行[1651:114465] touchesBegan-----
    其他用法 - 延迟执行[1651:114465] run-----
    

    3.3 一次性代码

    使用dispatch_once 函数能保证某段代码在程序运行过程中只被执行1次,适合做资源的加载

    static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
         // 只执行1次的代码(这里面默认是线程安全的)
      });
    }
    

    3.4 快速迭代

    使用dispatch_apply函数能进行快速迭代遍历:

    dispatch_apply(<#size_t iterations#>, <#dispatch_queue_t queue#>, ^(size_t) {
        //  代码
    });
    
    dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index){
        // 执行10次代码,index顺序不确定
    });
    

    代码示例:文件剪切

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
      [super viewDidLoad];
      // Do any additional setup after loading the view, typically from a nib.
    }
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
      [self apply];
    }
    
    #pragma mark - 文件剪切方法1:快速迭代
    - (void)apply {
    
      dispatch_queue_t queue =
          dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
      NSString *from = @"/Users/TD/Desktop/From";
      NSString *to = @"/Users/TD/Desktop/To";
    
      NSFileManager *mgr = [NSFileManager defaultManager];
      NSArray *subpaths = [mgr subpathsAtPath:from];
    
      dispatch_apply(subpaths.count, queue, ^(size_t index) {
    
        NSString *subpath = subpaths[index];
        NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
        NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
    
        // 剪切
        [mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
    
        NSLog(@"%@---%@", [NSThread currentThread], subpath);
      });
    }
    
    #pragma mark - 文件剪切方法2:传统方式
    - (void)moveFile {
      NSString *from = @"/Users/TD/Desktop/From";
      NSString *to = @"/Users/TD/Desktop/To";
    
      NSFileManager *mgr = [NSFileManager defaultManager];
    
      //获取文件夹下的所有文件路径,包括子文件夹下的文件路径
      NSArray *subpaths = [mgr subpathsAtPath:from];
    
      for (NSString *subpath in subpaths) {
    
        //全路径
        NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
        NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
    
        dispatch_async(
            dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
              // 剪切
              [mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
            });
      }
    }
    
    - (void)didReceiveMemoryWarning {
      [super didReceiveMemoryWarning];
      // Dispose of any resources that can be recreated.
    }
    
    @end
    

    3.5 队列组/调度组

    有这么一种需求:

    首先:分别异步执行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(),
    ^{
       // 等前面的异步操作都执行完毕后,回到主线程...
    });
    

    代码结构:

    其他用法 - 队列组调度组.png

    代码示例:

    #import "ViewController.h"
    
    @interface ViewController ()
    @property(weak, nonatomic) IBOutlet UIImageView *imageView;
    @property(nonatomic, strong) UIImage *image1; //图片1
    @property(nonatomic, strong) UIImage *image2; //图片2
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
      [super viewDidLoad];
      // Do any additional setup after loading the view, typically from a nib.
    }
    
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    
      dispatch_queue_t queue =
          dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
      // 创建一个队列组
      dispatch_group_t group = dispatch_group_create();
    
      // 1.下载图片1
      dispatch_group_async(group, queue, ^{
        // 图片的网络路径
        NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
        // 加载图片
        NSData *data = [NSData dataWithContentsOfURL:url];
        // 生成图片
        self.image1 = [UIImage imageWithData:data];
      });
    
      // 2.下载图片2
      dispatch_group_async(group, queue, ^{
        // 图片的网络路径
        NSURL *url = [NSURL URLWithString: @"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
        // 加载图片
        NSData *data = [NSData dataWithContentsOfURL:url];
        // 生成图片
        self.image2 = [UIImage imageWithData:data];
      });
    
      // 3.将图片1、图片2合成一张新的图片(也可以直接在此处回到主线程,只不过是因为绘制图片比较耗时,没有放在主线程而已)
      dispatch_group_notify(group, queue, ^{
    
        // 开启新的图形上下文
        UIGraphicsBeginImageContext(CGSizeMake(100, 100));
    
        // 绘制图片
        [self.image1 drawInRect:CGRectMake(0, 0, 50, 100)];
        [self.image2 drawInRect:CGRectMake(50, 0, 50, 100)];
    
        // 取得上下文中的图片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    
        // 结束上下文
        UIGraphicsEndImageContext();
    
        // 回到主线程显示图片
        dispatch_async(dispatch_get_main_queue(), ^{
          // 4.将新图片显示出来
          self.imageView.image = image;
        });
      });
    }
    
    - (void)didReceiveMemoryWarning {
      [super didReceiveMemoryWarning];
      // Dispose of any resources that can be recreated.
    }
    
    @end
    

    本文源码 Demo 详见 Github
    https://github.com/shorfng/iOS_4.0_multithreading.git




    作者:蓝田(Loto)
    出处: 简书

    如果你觉得本篇文章对你有所帮助,请点击文章末尾下方“喜欢”
    如有疑问,请通过以下方式交流:
    评论区回复微信(加好友请注明“简书+称呼”)发送邮件shorfng@126.com



    本文版权归作者和本网站共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。

    相关文章

      网友评论

        本文标题:4.4 GCD->3.0 其他用法

        本文链接:https://www.haomeiwen.com/subject/pflalttx.html