iOS开发基础112-GCD常见场景

Grand Central Dispatch(GCD)在iOS中的常见运用场景

GCD是Apple提供的多线程编程技术,旨在提供高效、轻量级的方式来执行并发任务。GCD使得管理线程变得简单且提高了应用程序的性能。以下是GCD在iOS中的一些常见运用场景,并详细介绍其底层原理。

1. 异步任务处理

场景:网络请求

使用GCD进行异步网络请求,使UI不被阻塞。

示例代码:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://example.com"]];
    dispatch_async(dispatch_get_main_queue(), ^{
        // 更新UI
        self.imageView.image = [UIImage imageWithData:data];
    });
});

原理解析:

  1. dispatch_async: 提交任务到一个队列,且不阻塞当前线程。
  2. dispatch_get_global_queue: 获取全局并发队列。
  3. dispatch_get_main_queue: 获取主队列,以在主线程上更新UI。

GCD的底层通过创建和管理轻量级的任务单元(blocks),并将这些任务提交到由系统管理的低层次队列(dispatch queues)。系统确保这些任务在适当的线程上执行,从而优化性能和资源使用。

2. 并发执行任务

场景:批量图片下载

示例代码:

dispatch_group_t group = dispatch_group_create();

for (NSString *urlString in urlArray) {
    dispatch_group_enter(group);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
        if (data) {
            UIImage *image = [UIImage imageWithData:data];
            // 处理图片
        }
        dispatch_group_leave(group);
    });
}

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // 所有任务完成后回到主线程
    [self updateUI];
});

原理解析:

  1. dispatch_group_create: 创建调度组。
  2. dispatch_group_enter: 进入调度组。
  3. dispatch_group_leave: 离开调度组。
  4. dispatch_group_notify: 所有调度组内任务完成后执行特定任务。

GCD的调度组允许将多个任务组合在一起,追踪这些任务的完成情况。当所有任务完成后,可以进行统一的后续操作。

3. 任务的延时执行

场景:避免频繁的UI更新(防抖)

示例代码:

dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC);
dispatch_after(delay, dispatch_get_main_queue(), ^{
    // 延时1秒执行的任务
    [self performUIUpdate];
});

原理解析:

  1. dispatch_time: 计算未来的时间点。
  2. dispatch_after: 在指定延迟后执行任务。

GCD处理定时任务时,底层会创建一个计时器,并在计时结束后将任务提交到相应的队列中执行。

4. 一次性执行

场景:单例模式

示例代码:

+ (instancetype)sharedInstance {
    static dispatch_once_t onceToken;
    static MyClass *instance = nil;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

原理解析:

  1. dispatch_once: 确保代码块只执行一次。

dispatch_once使用底层的 atomic operations 确保线程安全,即使在并发访问的情况下也能保证代码块只被执行一次。

5. 线程同步

场景:访问共享资源

示例代码:

@property (nonatomic, strong) dispatch_queue_t syncQueue;

- (instancetype)init {
    self = [super init];
    if (self) {
        _syncQueue = dispatch_queue_create("com.example.syncQueue", DISPATCH_QUEUE_SERIAL);
    }
    return self;
}

- (void)updateSharedResource {
    dispatch_sync(self.syncQueue, ^{
        // 访问和修改共享资源
    });
}

原理解析:

  1. dispatch_queue_create: 创建一个串行队列。
  2. dispatch_sync: 同步执行任务。

使用同步队列确保对共享资源的访问和修改是线程安全的。GCD通过串行化执行保证数据库访问、文件读写等不发生竞争情况。

6. 定期任务

场景:游戏中的刷新逻辑或实时更新

示例代码:

@property (nonatomic, strong) dispatch_source_t timer;

- (void)startTimer {
    dispatch_queue_t queue = dispatch_get_main_queue();
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0);
    dispatch_source_set_event_handler(self.timer, ^{
        // 定期运行的任务
        [self updateGameState];
    });
    dispatch_resume(self.timer);
}

- (void)stopTimer {
    dispatch_source_cancel(self.timer);
    self.timer = NULL;
}

原理解析:

  1. dispatch_source_create: 创建调度源,常用于计时器、文件描述符、信号等。
  2. dispatch_source_set_timer: 设置计时器的开始时间和重复间隔。
  3. dispatch_source_set_event_handler: 设置事件处理程序。

GCD的调度源基于"run loop"机制,可以高效地处理定期任务,同时通过驻留的内核对象来管理计时器等资源。

7. 信号量控制

场景:控制并发数量

示例代码:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(3); // 最大并发数为3

for (NSString *urlString in urlArray) {
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
        if (data) {
            UIImage *image = [UIImage imageWithData:data];
            // 处理图片
        }
        dispatch_semaphore_signal(semaphore);
    });
}

原理解析:

  1. dispatch_semaphore_create: 创建信号量。
  2. dispatch_semaphore_wait: 等待信号。
  3. dispatch_semaphore_signal: 发送信号。

信号量通过计数机制控制并发线程的数量,GCD底层使用信号量的原子操作确保线程安全。

8. Barrier Block

场景:多读单写

示例代码:

dispatch_queue_t queue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{
    // 读操作
    [self readData];
});

dispatch_async(queue, ^{
    // 读操作
    [self readData];
});

dispatch_barrier_async(queue, ^{
    // 写操作
    [self writeData];
});

dispatch_async(queue, ^{
    // 读操作
    [self readData];
});

原理解析:

  1. DISPATCH_QUEUE_CONCURRENT: 创建并发队列。
  2. dispatch_barrier_async: 提交栅栏任务,确保它之前和之后的并发任务都完成,将其与其他任务隔离开来。

Barrier Block允许在并发队列中实现类似于数据库的多读单写机制,从而确保资源的一致性。

9. 主队列死锁问题

场景:防止死锁

示例代码:

// 在主队列上同步提交任务会导致死锁
dispatch_sync(dispatch_get_main_queue(), ^{
    NSLog(@"This will never be printed");
});

原理解析:

  1. 主队列是串行队列,如果在主线程上同步提交任务,会导致死锁,因为主线程自身正在等待提交的任务完成。

避免在主线程上同步提交任务,否则会导致严重的阻塞问题。GCD的底层实现通过检查pthread_main_np来判定当前是否在主线程上进行阻塞操作,如是则抛出异常或警告。

总结

GCD通过诸如dispatch queues、dispatch groups、dispatch sources和semaphores等基础设施,提供了一种高效、优雅的并发编程方式。其底层主要基于任务调度器、线程池和内核对象(如计时器、信号量)等组成。通过合理使用这些组件,开发者可以显著提高应用程序的响应速度和资源利用率。

posted @   Mr.陳  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
历史上的今天:
2015-07-17 iOS开发基础10-UIButton内边距和图片拉伸模式
2015-07-17 iOS开发基础9-提示框(UIAlertController)
点击右上角即可分享
微信分享提示