作者归档 guhuangwudi@gmail.com

通过guhuangwudi@gmail.com

GCD 编程(转)

  • GCD串行队列与并发队列
  • GCD延时执行
  • GCD线程组
  • GCD定时器
  • GCD信号量
  • GCD综合使用示例
  • GCD串行队列与并发队列:
    串行队列一次只执行一个线程,按照添加到队列的顺序依次执行。
    并发队列一次可以执行多个线程,线程的执行没有先后顺序。
    UI界面所在的线程队列是串行队列:即业务逻辑在子线程中处理,界面刷新回到主线程
  • 并发队列
 *  并发队列
 */
- (void)initConcurrent
{
    // 创建串行队列
    GCDQueue *queue = [[GCDQueue alloc] initConcurrent];

    // 执行队列中的线程
    [queue execute:^{
        NSLog(@"1");
    }];

    // 执行队列中的线程
    [queue execute:^{
        NSLog(@"2");
    }];

    // 执行队列中的线程
    [queue execute:^{
        NSLog(@"3");
    }];

    // 执行队列中的线程
    [queue execute:^{
        NSLog(@"4");
    }];

    // 执行队列中的线程
    [queue execute:^{
        NSLog(@"5");
    }];

}
  • 打印结果:

    Paste_Image.png

    按顺序执行

  • 并发队列
/**
 *  并发队列
 */
- (void)initConcurrent
{
    // 创建串行队列
    GCDQueue *queue = [[GCDQueue alloc] initConcurrent];

    // 执行队列中的线程
    [queue execute:^{
        NSLog(@"1");
    }];

    // 执行队列中的线程
    [queue execute:^{
        NSLog(@"2");
    }];

    // 执行队列中的线程
    [queue execute:^{
        NSLog(@"3");
    }];

    // 执行队列中的线程
    [queue execute:^{
        NSLog(@"4");
    }];

    // 执行队列中的线程
    [queue execute:^{
        NSLog(@"5");
    }];

}
  • 打印结果:

Paste_Image.png

无顺序执行

  • 子线程处理业务逻辑,回到主线程刷新UI
- (void)viewDidLoad {
    [super viewDidLoad];

//    [self serailQueue];

    [self initConcurrent];

    self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    self.imageView.center = self.view.center;
    [self.view addSubview:self.imageView];

    [GCDQueue executeInGlobalQueue:^{

        // 子线程中处理业务逻辑
        NSString *pictureUrl = @"http://upload-images.jianshu.io/upload_images/189984-89d7604ddd8f90b5.png";
        NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:pictureUrl]];
       NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
        self.image = [UIImage imageWithData:data];
        NSLog(@"处理业务逻辑");

        [GCDQueue executeInMainQueue:^{
            // 主线程中更新UI
            self.imageView.image = self.image;
            NSLog(@"更新UI");
        }];

    }];

}
  • GCD延时执行
 // GCD方式的延时执行操作
    [GCDQueue executeInMainQueue:^{
        NSLog(@"GCD延时线程事件");
    } afterDelaySecs:2.f];
  • 与NSThread方法的延时执行进行比较
// NSThread方式延迟操作
    [self performSelector:@selector(threadEvent:) withObject:self afterDelay:2.f];
  • 取消
// 取消延时执行操作
    [NSObject cancelPreviousPerformRequestsWithTarget:self];

比较结果:

Paste_Image.png
  • GCD延时执行的优缺点
    优点:代码简洁
    缺点:精确度低,不能取消
  • GCD 线程组
  • GCD 线程组的用处
  • GCD 线程组进行事件监听
  • GCD 线程组使用的场景
- (void)viewDidLoad {
    [super viewDidLoad];

    // 等待 线程一 与 线程二 执行完毕再去执行线程三
    // GCD线程组
    // 初始化线程组
    GCDGroup *group = [[GCDGroup alloc] init];
    // 初始化队列
    GCDQueue *queue = [[GCDQueue alloc] init];
    // 添加事件
    [queue execute:^{
        sleep(1);   // 休眠一秒
        NSLog(@"线程一事件完毕");
    } inGroup:group];

    [queue execute:^{
        sleep(3);   // 休眠三秒执行
        NSLog(@"线程二事件执行完毕");
    } inGroup:group];

    // 监听事件一和时间二执行完毕之后执行事件三
    [queue notify:^{
        NSLog(@"线程三事件执行完毕");
    } inGroup:group];

}
  • 运行结果:

    Paste_Image.png
  • GCD定时器
  • 构建并使用GCD定时器
/**
 *  GCD定时器
 */
- (void)runGCDTimer
{
    // 初始化定时器
    self.gcdTimer = [[GCDTimer alloc] initInQueue:[GCDQueue mainQueue]];
    // 指定时间间隔以及要执行的事件
    [self.gcdTimer event:^{
        NSLog(@"GCD定时器");
    } timeInterval:NSEC_PER_SEC];  // 一秒执行
    // 运行GCD定时器
    [self.gcdTimer start];
}
  • NSTimer定时器
/**
 *  NSTimer定时器
 */
- (void)runNormalTimer
{
    self.normalTimer = [NSTimer scheduledTimerWithTimeInterval:1.f target:self selector:@selector(runNSTimer) userInfo:nil repeats:YES];
}

- (void)runNSTimer
{
    NSLog(@"运行NSTimer定时器");
}
  • GCD定时器和NSTimer定时器的一些区别
    区别:NSTimer是运行在runloop中的,如果在TableView中使用了NSTimer定时器,会出现一些奇怪的问题,而使用GCD定时器,则不会存在这些问题
  • GCD信号量
  • 构建并使用GCD信号量
  • 用GCD信号量将异步线程转化为同步线程
- (void)viewDidLoad {
    [super viewDidLoad];

//    // 创建信号量
    GCDSemaphore *semaphore = [[GCDSemaphore alloc] init];
//    // 发送信号
//    [semaphore signal];
//    // 等待信号(注意:发送信号和等待信号是同步使用的)
//    [semaphore wait];

    // 线程1 - 异步
    [GCDQueue executeInGlobalQueue:^{
        NSLog(@"1");
        [semaphore signal]; // 执行完毕,发送信号
    }];

    // 线程2 - 异步
    [GCDQueue executeInGlobalQueue:^{
        [semaphore wait];   // 等待信号,收到之后再执行
        NSLog(@"2");
    }];

    // 必须先执行完线程一,再执行线程二
}

@end
  • 运行结果

Paste_Image.png

有了信号量的检测,我们发现只有在线程一执行完毕才会执行线程二,这种情况在真实项目中开发不多见,但是极端的情况下就会发现信号量是解决异步线程先后顺序的利器!

  • GCD综合使用示例
  • 用并发下载多张图片来演示GCD的强大之处
  • 异步下载三张图片
#import "ViewController.h"
#import "GCD.h"

@interface ViewController ()

@property (nonatomic, strong) UIImageView *view1;
@property (nonatomic, strong) UIImageView *view2;
@property(nonatomic, strong) UIImageView * view3;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // 初始化三个imageView用来显示图片
    self.view1 = [self createImageViewWithFrame:CGRectMake(0, 0, 100, 100)];
    self.view2 = [self createImageViewWithFrame:CGRectMake(100, 0, 100, 100)];
    self.view3 = [self createImageViewWithFrame:CGRectMake(200, 0, 100, 100)];

    // 初始化三个网址
    NSString *net1 = @"http://ww2.sinaimg.cn/large/68d8771bgw1eupg6yqzzvj20m80etgo5.jpg";
    NSString *net2 = @"http://ww4.sinaimg.cn/large/68d8771bgw1eupg7b3r92j20eu0m7n07.jpg";
    NSString *net3 = @"http://ww3.sinaimg.cn/large/68d8771bgw1eupg7oceo7j20cy0ihn27.jpg";

    // 初始化信号量
    GCDSemaphore *semaphore = [[GCDSemaphore alloc] init];

    // 图片下载
    [GCDQueue executeInGlobalQueue:^{
        UIImage *image1 = [self accessDataImageByNetString:net1];

       [GCDQueue executeInMainQueue:^{

           // 动画
           [UIView animateWithDuration:2.f animations:^{
               self.view1.image = image1;
               self.view1.alpha = 1.f;

           } completion:^(BOOL finished) {
               // 第一张图片完全显示完毕发送信号
               [semaphore signal];
           }];
       }];
    }];

    [GCDQueue executeInGlobalQueue:^{
        UIImage *image2 = [self accessDataImageByNetString:net2];

        // 阻塞线程,等待第一张图片的下载完毕执行动画
        [semaphore wait];

        [GCDQueue executeInMainQueue:^{
            [UIView animateWithDuration:2.f animations:^{

                self.view2.image = image2;
                self.view2.alpha = 1.f;
            } completion:^(BOOL finished) {
                [semaphore signal];
            }];
        }];
    }];

    [GCDQueue executeInGlobalQueue:^{
        UIImage *image3 = [self accessDataImageByNetString:net3];

        // 阻塞线程,等待第二张图片下载完毕执行动画
        [semaphore wait];
        [GCDQueue executeInMainQueue:^{

            [UIView animateWithDuration:2.f animations:^{
                self.view3.image = image3;
                self.view3.alpha = 1.f;
            }];
        }];
    }];

}

/**
 *  获取网络图片
 *
 *  @param netString 图片网址
 *
 *  @return 图片
 */
- (UIImage *)accessDataImageByNetString:(NSString *)netString
{
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:netString]];
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
    UIImage *image = [[UIImage alloc] initWithData:data];
    return image;
}

/**
 *  创建ImageView
 *
 *  @param frame frame
 *
 *  @return imageView
 */
- (UIImageView *)createImageViewWithFrame:(CGRect)frame
{
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
    imageView.alpha = 0.f;
    [self.view addSubview:imageView];
    return imageView;
}

@end
  • 运行结果

    GCD.gif
  • 用线程组实现
GCDGroup *group = [[GCDGroup alloc] init];
    GCDQueue *queue = [[GCDQueue alloc] init];
    [queue execute:^{
        UIImage *image1 = [self accessDataImageByNetString:net1];
        [GCDQueue executeInMainQueue:^{

            [UIView animateWithDuration:2.f animations:^{
                self.view1.image = image1;
                self.view1.alpha = 1.f;

            } completion:^(BOOL finished) {

            }];
        }];
    } inGroup:group];
    [queue execute:^{
        UIImage *image2 = [self accessDataImageByNetString:net2];
        [GCDQueue executeInMainQueue:^{
            [UIView animateWithDuration:2.f animations:^{
                self.view2.image = image2;
                self.view2.alpha = 1.f;
            } completion:^(BOOL finished) {

            }];
        }];
    } inGroup:group];
    [queue notify:^{
        UIImage *image3 = [self accessDataImageByNetString:net3];
        [GCDQueue executeInMainQueue:^{
            [UIView animateWithDuration:2.f animations:^{

                self.view3.image = image3;
                self.view3.alpha = 1.f;
            } completion:^(BOOL finished) {

            }];
        }];
    } inGroup:group];
  • 掌握
  • GCD串行队列与并发队列
  • GCD延时执行操作
  • GCD线程组的使用
  • GCD定时器的使用
  • 用GCD信号量将异步操作转化为同步操作