IOS-线程(GCD)

一、GCD的使用

//
//  IBController3.m
//  IBCoder1
//
//  Created by Bowen on 2018/4/25.
//  Copyright © 2018年 BowenCoder. All rights reserved.
//

#import "IBController3.h"

@interface IBController3 ()

@end

/*
 进程:执行中的应用程序【一个应用程序可以有很多进程(网游双开),在iOS中一个app只有一个进程。】
 资源分配最小单元
 线程:进程中的一个执行序列,执行调度的最小单元
 
 线程与进程的区别:
 a.地址空间和其它资源:进程间拥有独立内存,进程是资源分配的基本单位;线程隶属于某一进程,且同一进程的各线程间共享内存(资源),线程是cpu调度的基本单位。
 b.通信:进程间相互独立,通信困难,常用的方法有:管道,信号,套接字,共享内存,消息队列等;线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
 c.调度和切换:线程上下文切换比进程上下文切换要快。进程间切换要保存上下文,加载另一个进程;而线程则共享了进程的上下文环境,切换更快。
 
 一、简介
 1.什么是GCD
 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器”
 纯C语言,提供了非常多强大的函数
 
 2.GCD的优势
 GCD是苹果公司为多核的并行运算提出的解决方案
 GCD会自动利用更多的CPU内核(比如双核、四核)
 GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
 
 二、任务和队列
 1.GCD中有2个核心概念
 任务:执行什么操作
 队列:用来存放任务
 
 2.GCD的使用就2个步骤
 定制任务
 确定想做的事情
 
 3.将任务添加到队列中
 GCD会自动将队列中的任务取出,放到对应的线程中执行
 任务的取出遵循队列的FIFO原则:先进先出,后进后出
 
 三、执行任务
 1.GCD中有2个用来执行任务的函数
 用同步的方式执行任务
 dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
 queue:队列
 block:任务
 
 2.用异步的方式执行任务
 dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
 
 3.同步和异步的区别
 同步:只能在当前线程中执行任务,不具备开启新线程的能力
 异步:可以在新的线程中执行任务,具备开启新线程的能力
 
 四、队列的类型
 1.GCD的队列可以分为2大类型
 并发队列(Concurrent Dispatch Queue)
 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
 并发功能只有在异步(dispatch_async)函数下才有效
 
 2.串行队列(Serial Dispatch Queue)
 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
 
 五、容易混淆的术语
 1.有4个术语比较容易混淆:同步、异步、并发、串行
 同步和异步主要影响:能不能开启新的线程
 同步:在当前线程中执行任务,不具备开启新线程的能力
 异步:在新的线程中执行任务,具备开启新线程的能力
 
 2.并发和串行主要影响:任务的执行方式
 并发:多个任务并发(同时)执行
 串行:一个任务执行完毕后,再执行下一个任务
 
 六、并发队列
 1.GCD默认已经提供了全局的并发队列,供整个应用使用,不需要手动创建
 使用dispatch_get_global_queue函数获得全局的并发队列
 dispatch_queue_t dispatch_get_global_queue(
 dispatch_queue_priority_t priority, // 队列的优先级
 unsigned long flags); // 此参数暂时无用,用0即可
 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 获得全局并发队列
 
 2.全局并发队列的优先级
 #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 // 后台
 
 七、串行队列
 1.GCD中获得串行有2种途径
 使用dispatch_queue_create函数创建串行队列
 dispatch_queue_t
 dispatch_queue_create(const char *label, // 队列名称
 dispatch_queue_attr_t attr); // 队列属性,一般用NULL即可
 dispatch_queue_t queue = dispatch_queue_create("cn.itcast.queue", NULL); // 创建
 dispatch_release(queue); // 非ARC需要释放手动创建的队列
 
 2.使用主队列(跟主线程相关联的队列)
 主队列是GCD自带的一种特殊的串行队列
 放在主队列中的任务,都会放到主线程中执行
 使用dispatch_get_main_queue()获得主队列
 dispatch_queue_t queue = dispatch_get_main_queue();
 
 八、各种队列的执行效果
 注意
 使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列
 
 九、线程间通信示例
 1.从子线程回到主线程
 dispatch_async(
 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 // 执行耗时的异步操作...
 dispatch_async(dispatch_get_main_queue(), ^{
 // 回到主线程,执行UI刷新操作
 });
 });
 
 十、延时执行
 iOS常见的延时执行有2种方式
 调用NSObject的方法
 [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
 // 2秒后再调用self的run方法
 
 使用GCD函数
 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
 // 2秒后执行这里的代码... 在哪个线程执行,跟队列类型有关
 
 });
 
 十一、一次性代码
 使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次
 static dispatch_once_t onceToken;
 dispatch_once(&onceToken, ^{
 // 只执行1次的代码(这里面默认是线程安全的)
 });
 
 十二、队列组
 1.有这么1种需求
 首先:分别异步执行2个耗时的操作
 其次:等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(), ^{
 // 等前面的异步操作都执行完毕后,回到主线程...
 });
 
 十三、Core Foundation
 Foundation : OC
 Core Foundation : C
 Foundation和Core Foundation可以相互转化
 
 NSString *str = @"123";
 CFStringRef str2 = (__bridge CFStringRef)str;
 NSString *str3 = (__bridge NSString *)(str2);
 
 CFArrayRef --- NSArray
 CFDictionaryRef --- NSDirectory
 CFNumberRef --- NSNumber
 
 凡是函数名中带有createcopy
etain
ew等字眼,都应该再不需要的时候进行release
 GCD的数据类型在ARC环境下不需要release
 CF(Core Foundation)的数据类型在ARC环境下还需要release
 
 Core Foundation中手动创建的数据类型,需要手动释放
 CFArrayRef array = CFArrayCreate(NULL, NULL, 10, NULL);
 CFRelease(array);
 
 CGPathRef path = CGPathCreateMutable();
 CGPathRelease(path);
 
 
 注意千万分清:主线程和主队列概念

 
 */

@implementation IBController3

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSLog(@"-------------------------------------");

//    [self test1];
//    [self test2];
//    [self test3];
//    [self test4];
//    [self test5];
//    [self test6];
    [self test7];
//    [self test8];
//    [self test9];
    NSLog(@"++++++++++++++++++++++++++++++++++++");
    
}

/**
 同步主队列
 在主线程中执行
 理解:主队列在同步任务的条件下,必须主线程空闲的时候,才可以添加任务到队列中
 */
- (void)test9 {
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        NSLog(@"----1----%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"----2----%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"----3----%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"----4----%@",[NSThread currentThread]);
    });
}

/**
 * sync(同步) --串行队列
 * 会不会创建线程:不会
 * 线程的执行方法:串行(一个任务执行完毕后,再执行下一个任务)
 * 在主线程中执行串行队列,完成后回到主线程在执行主队列
 */
- (void)test8 {
    dispatch_queue_t queue = dispatch_queue_create("bowen", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        NSLog(@"----1----%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"----2----%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"----3----%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"----4----%@",[NSThread currentThread]);
    });
}

/**
 * sync(同步) --并发队列
 * 会不会创建线程:不会,在主线程中运行
 * 任务的执行方式:串行(一个任务执行完毕后,再执行下一个任务)
 * 并发队列失去并发功能
 * 在主线程中执行并发队列,完成后回到主线程执行主队列
 */
- (void)test7 {
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_sync(queue, ^{
        NSLog(@"----1----%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"----2----%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"----3----%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"----4----%@",[NSThread currentThread]);
    });

}

/**
 * async(异步) --主队列(很常用)(特殊)
 * 会不会创建线程:不会
 * 任务的执行方式:串行
 * 一般用在线程之间的通讯
 * 理解:异步主队列,先把任务添加到主队列中,等主线程空闲执行任务
 */
- (void)test6 {
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_async(queue, ^{
        NSLog(@"----1----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----2----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----3----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----4----%@",[NSThread currentThread]);
    });
}

/**
 * async(异步) --串行队列(有时用)
 * 会不会创建线程:会,创建一条
 * 线程的执行方法:串行(一个任务执行完毕后,再执行下一个任务)
 */
- (void)test5 {
    dispatch_queue_t queue = dispatch_queue_create("bowen", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        NSLog(@"----1----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----2----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----3----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----4----%@",[NSThread currentThread]);
    });

}



/**
 * async(异步) --并发队列(最常用)
 * 会不会创建线程:会,并且创建多条线程
 * 任务的执行方式:并发执行
 */
- (void)test4 {
    
    dispatch_queue_t queue = dispatch_queue_create("bowen", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"----1----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----2----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----3----%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----4----%@",[NSThread currentThread]);
    });
    
}

/**
 组内并行,组间串行
 */
- (void)test1 {
    
    dispatch_group_t group1 = dispatch_group_create();
    dispatch_queue_t queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_group_async(group1, queue1, ^{
        NSLog(@"group1 --- 1---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group1, queue1, ^{
        NSLog(@"group1 --- 2---%@",[NSThread currentThread]);

    });
    dispatch_group_async(group1, queue1, ^{
        NSLog(@"group1 --- 3---%@",[NSThread currentThread]);

    });
    dispatch_group_async(group1, queue1, ^{
        NSLog(@"group1 --- 4---%@",[NSThread currentThread]);

    });
    
    dispatch_group_wait(group1,DISPATCH_TIME_FOREVER);
    
    dispatch_group_t group2 = dispatch_group_create();
    dispatch_queue_t queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_group_async(group2, queue2, ^{
        NSLog(@"group2 --- 1---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group2, queue2, ^{
        NSLog(@"group2 --- 2---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group2, queue2, ^{
        NSLog(@"group2 --- 3---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group2, queue2, ^{
        NSLog(@"group2 --- 4---%@",[NSThread currentThread]);
    });
}


- (void)test2 {
    NSOperationQueue *oq = [[NSOperationQueue alloc] init];
    oq.maxConcurrentOperationCount = 1;
    [oq addOperationWithBlock:^{
        NSLog(@"1--%@",[NSThread currentThread]);
    }];
    [oq addOperationWithBlock:^{
        NSLog(@"2--%@",[NSThread currentThread]);
    }];
    [oq addOperationWithBlock:^{
        NSLog(@"3--%@",[NSThread currentThread]);
    }];
    [oq addOperationWithBlock:^{
        NSLog(@"4--%@",[NSThread currentThread]);
    }];
}

/**
 组内串行,组间并行
 */
- (void)test3 {
    
    dispatch_group_t group1 = dispatch_group_create();
    dispatch_queue_t queue1 = dispatch_queue_create("leador", DISPATCH_QUEUE_SERIAL);
    dispatch_group_async(group1, queue1, ^{
        NSLog(@"group1 --- 1---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group1, queue1, ^{
        NSLog(@"group1 --- 2---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group1, queue1, ^{
        NSLog(@"group1 --- 3---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group1, queue1, ^{
        NSLog(@"group1 --- 4---%@",[NSThread currentThread]);
    });
    
    
    dispatch_group_t group2 = dispatch_group_create();
    dispatch_queue_t queue2 = dispatch_queue_create("leador1", DISPATCH_QUEUE_SERIAL);

    dispatch_group_async(group2, queue2, ^{
        NSLog(@"group2 --- 1---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group2, queue2, ^{
        NSLog(@"group2 --- 2---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group2, queue2, ^{
        NSLog(@"group2 --- 3---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group2, queue2, ^{
        NSLog(@"group2 --- 4---%@",[NSThread currentThread]);
    });
}


@end

二、GCD线程之间通信

 1 //
 2 //  ViewController.m
 3 //  IOS_0117_GCD通信
 4 //
 5 //  Created by ma c on 16/1/17.
 6 //  Copyright (c) 2016年 博文科技. All rights reserved.
 7 //
 8 
 9 #import "ViewController.h"
10 
11 @interface ViewController ()
12 
13 @property (nonatomic, strong) UIButton *btn;
14 
15 @end
16 
17 @implementation ViewController
18 
19 - (void)viewDidLoad {
20     [super viewDidLoad];
21     
22     self.btn = [UIButton buttonWithType:UIButtonTypeCustom];
23     [self.btn setFrame:CGRectMake(100, 150, 150, 150)];
24     [self.btn setBackgroundColor:[UIColor purpleColor]];
25     [self.view addSubview:self.btn];
26 }
27 
28 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
29 {
30     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
31     dispatch_async(queue, ^{
32         
33         NSLog(@"download-----%@",[NSThread currentThread]);
34         //子线程下载图片
35         NSURL *url = [NSURL URLWithString:@"http://www.qiyue.com/uploadfile/2016/0103/20160103105808532.jpg"];
36         NSData *data = [NSData dataWithContentsOfURL:url];
37         UIImage *image = [UIImage imageWithData:data];
38         
39         //回到主线程
40         dispatch_queue_t mainQueue = dispatch_get_main_queue();
41         dispatch_async(mainQueue, ^{
42             
43             NSLog(@"刷新---------%@---%@",[NSThread currentThread],image);
44             [self.btn setBackgroundImage:image forState:UIControlStateNormal];
45             
46         });
47     });
48 }
49 
50 @end

 三、延迟执行

 1 //
 2 //  ViewController.m
 3 //  IOS_0117_GCD线程通信
 4 //
 5 //  Created by ma c on 16/1/17.
 6 //  Copyright (c) 2016年 博文科技. All rights reserved.
 7 //
 8 
 9 #import "ViewController.h"
10 
11 @interface ViewController ()
12 
13 @end
14 
15 @implementation ViewController
16 
17 - (void)viewDidLoad {
18     [super viewDidLoad];
19     // Do any additional setup after loading the view, typically from a nib.
20 }
21 
22 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
23 {
24 //    [self delay1];
25 //    [self delay2];
26     
27 
28 }
29 
30 - (void)delay3
31 {
32     NSLog(@"-----touchesBegan1------");
33     //3s后回到主线程执行block代码
34     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
35         NSLog(@"-----task------%@",[NSThread currentThread]);
36     });
37     
38     //3s后自动开启新线程执行block代码
39     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
40     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), queue, ^{
41         NSLog(@"-----task------%@",[NSThread currentThread]);
42     });
43     
44     NSLog(@"-----touchesBegan2------");
45 }
46 
47 - (void)delay2
48 {
49     NSLog(@"-----touchesBegan1------");
50     
51     //定制好延迟任务后,不会卡主当前线程
52     [self performSelector:@selector(run) withObject:@"http://" afterDelay:3];
53     
54     NSLog(@"-----touchesBegan2------");
55 }
56 
57 - (void)run:(NSString *)url
58 {
59     NSLog(@"run------%@---%@",[NSThread currentThread], url);
60 }
61 
62 - (void)delay1
63 {
64     NSLog(@"------begin------");
65     
66     //延迟线程不要用sleep,坏处:卡主线程
67     [NSThread sleepForTimeInterval:3];
68     NSLog(@"下载图片");
69 }
70 @end

四、GCD一次性代码

 1 //
 2 //  ViewController.m
 3 //  IOS_0117_GCD一次性代码
 4 //
 5 //  Created by ma c on 16/1/17.
 6 //  Copyright (c) 2016年 博文科技. All rights reserved.
 7 //
 8 
 9 #import "ViewController.h"
10 
11 @interface ViewController ()
12 
13 @property (nonatomic, assign) BOOL hasExecuted;
14 
15 @end
16 
17 @implementation ViewController
18 
19 - (void)viewDidLoad {
20     [super viewDidLoad];
21     // Do any additional setup after loading the view, typically from a nib.
22 }
23 
24 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
25 {
26 //    [self fun1];
27     [self fun2];
28 }
29 
30 - (void)fun2
31 {
32     static dispatch_once_t onceToken;
33     dispatch_once(&onceToken, ^{
34         
35         NSLog(@"下载图片");
36     });
37 }
38 - (void)fun1
39 {
40     if (self.hasExecuted) return;
41     
42     NSLog(@"下载图片");
43     self.hasExecuted = YES;
44 }
45 
46 @end

五、队列组

  1 //
  2 //  HMViewController.m
  3 //  03-队列组(了解)
  4 //
  5 //  Created by apple on 14-9-16.
  6 //  Copyright (c) 2014年 heima. All rights reserved.
  7 //
  8 
  9 #import "HMViewController.h"
 10 
 11 // 1.分别下载2张图片:大图片、LOGO
 12 // 2.合并2张图片
 13 // 3.显示到一个imageView身上
 14 
 15 @interface HMViewController ()
 16 @property (weak, nonatomic) IBOutlet UIImageView *imageView;
 17 @property (nonatomic, strong) UIImage *image1;
 18 @property (nonatomic, strong) UIImage *image2;
 19 @end
 20 
 21 @implementation HMViewController
 22 
 23 - (void)viewDidLoad
 24 {
 25     [super viewDidLoad];
 26     
 27 }
 28 
 29 // 2D绘图  Quartz2D
 30 // 合并图片 -- 水印
 31 
 32 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
 33 {
 34     // 1.队列组
 35     dispatch_group_t group = dispatch_group_create();
 36     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 37     
 38     // 2.下载图片1
 39     __block UIImage *image1 = nil;
 40     dispatch_group_async(group, queue, ^{
 41         NSURL *url1 = [NSURL URLWithString:@"http://g.hiphotos.baidu.com/image/pic/item/f2deb48f8c5494ee460de6182ff5e0fe99257e80.jpg"];
 42         NSData *data1 = [NSData dataWithContentsOfURL:url1];
 43         image1 = [UIImage imageWithData:data1];
 44     });
 45     
 46     // 3.下载图片2
 47     __block UIImage *image2 = nil;
 48     dispatch_group_async(group, queue, ^{
 49         NSURL *url2 = [NSURL URLWithString:@"http://su.bdimg.com/static/superplus/img/logo_white_ee663702.png"];
 50         NSData *data2 = [NSData dataWithContentsOfURL:url2];
 51         image2 = [UIImage imageWithData:data2];
 52     });
 53     
 54     // 4.合并图片 (保证执行完组里面的所有任务之后,再执行notify函数里面的block)
 55     dispatch_group_notify(group, queue, ^{
 56         // 开启一个位图上下文
 57         UIGraphicsBeginImageContextWithOptions(image1.size, NO, 0.0);
 58         
 59         // 绘制第1张图片
 60         CGFloat image1W = image1.size.width;
 61         CGFloat image1H = image1.size.height;
 62         [image1 drawInRect:CGRectMake(0, 0, image1W, image1H)];
 63         
 64         // 绘制第2张图片
 65         CGFloat image2W = image2.size.width * 0.5;
 66         CGFloat image2H = image2.size.height * 0.5;
 67         CGFloat image2Y = image1H - image2H;
 68         [image2 drawInRect:CGRectMake(0, image2Y, image2W, image2H)];
 69         
 70         // 得到上下文中的图片
 71         UIImage *fullImage = UIGraphicsGetImageFromCurrentImageContext();
 72         
 73         // 结束上下文
 74         UIGraphicsEndImageContext();
 75         
 76         // 5.回到主线程显示图片
 77         dispatch_async(dispatch_get_main_queue(), ^{
 78             self.imageView.image = fullImage;
 79         });
 80     });
 81 }
 82 
 83 - (void)test2
 84 {
 85     // 异步下载
 86     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 87         // 1.下载第1张
 88         NSURL *url1 = [NSURL URLWithString:@"http://g.hiphotos.baidu.com/image/pic/item/f2deb48f8c5494ee460de6182ff5e0fe99257e80.jpg"];
 89         NSData *data1 = [NSData dataWithContentsOfURL:url1];
 90         self.image1 = [UIImage imageWithData:data1];
 91         
 92         [self bindImages];
 93     });
 94     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 95         // 2.下载第2张
 96         NSURL *url2 = [NSURL URLWithString:@"http://su.bdimg.com/static/superplus/img/logo_white_ee663702.png"];
 97         NSData *data2 = [NSData dataWithContentsOfURL:url2];
 98         self.image2 = [UIImage imageWithData:data2];
 99         
100         [self bindImages];
101     });
102 }
103 
104 - (void)bindImages
105 {
106     if (self.image1 == nil || self.image2 == nil) return;
107     
108     // 3.合并图片
109     // 开启一个位图上下文
110     UIGraphicsBeginImageContextWithOptions(self.image1.size, NO, 0.0);
111     
112     // 绘制第1张图片
113     CGFloat image1W = self.image1.size.width;
114     CGFloat image1H = self.image1.size.height;
115     [self.image1 drawInRect:CGRectMake(0, 0, image1W, image1H)];
116     
117     // 绘制第2张图片
118     CGFloat image2W = self.image2.size.width * 0.5;
119     CGFloat image2H = self.image2.size.height * 0.5;
120     CGFloat image2Y = image1H - image2H;
121     [self.image2 drawInRect:CGRectMake(0, image2Y, image2W, image2H)];
122     
123     // 得到上下文中的图片
124     UIImage *fullImage = UIGraphicsGetImageFromCurrentImageContext();
125     
126     // 结束上下文
127     UIGraphicsEndImageContext();
128     
129     // 4.回到主线程显示图片
130     dispatch_async(dispatch_get_main_queue(), ^{
131         self.imageView.image = fullImage;
132     });
133 }
134 
135 - (void)test1
136 {
137     // 异步下载
138     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
139         // 1.下载第1张
140         NSURL *url1 = [NSURL URLWithString:@"http://g.hiphotos.baidu.com/image/pic/item/f2deb48f8c5494ee460de6182ff5e0fe99257e80.jpg"];
141         NSData *data1 = [NSData dataWithContentsOfURL:url1];
142         UIImage *image1 = [UIImage imageWithData:data1];
143         
144         // 2.下载第2张
145         NSURL *url2 = [NSURL URLWithString:@"http://su.bdimg.com/static/superplus/img/logo_white_ee663702.png"];
146         NSData *data2 = [NSData dataWithContentsOfURL:url2];
147         UIImage *image2 = [UIImage imageWithData:data2];
148         
149         // 3.合并图片
150         // 开启一个位图上下文
151         UIGraphicsBeginImageContextWithOptions(image1.size, NO, 0.0);
152         
153         // 绘制第1张图片
154         CGFloat image1W = image1.size.width;
155         CGFloat image1H = image1.size.height;
156         [image1 drawInRect:CGRectMake(0, 0, image1W, image1H)];
157         
158         // 绘制第2张图片
159         CGFloat image2W = image2.size.width * 0.5;
160         CGFloat image2H = image2.size.height * 0.5;
161         CGFloat image2Y = image1H - image2H;
162         [image2 drawInRect:CGRectMake(0, image2Y, image2W, image2H)];
163         
164         // 得到上下文中的图片
165         UIImage *fullImage = UIGraphicsGetImageFromCurrentImageContext();
166         
167         // 结束上下文
168         UIGraphicsEndImageContext();
169         
170         // 4.回到主线程显示图片
171         dispatch_async(dispatch_get_main_queue(), ^{
172             self.imageView.image = fullImage;
173         });
174     });
175 }
176 
177 @end
原文地址:https://www.cnblogs.com/oc-bowen/p/5137635.html