美文网首页
OC 和 Swift 关于GCD多线程的使用区别

OC 和 Swift 关于GCD多线程的使用区别

作者: 大成小栈 | 来源:发表于2025-07-15 11:50 被阅读0次

我们来详细对比一下 OCSwiftGrand Central Dispatch (GCD) 的多线程用法,特别是线程保活等常见场景,并通过代码示例说明。

核心概念一致

首先,最重要的一点是:GCD 的核心概念、队列类型(串行、并发、主队列、全局队列)、任务调度方式(同步sync / 异步async)、组(dispatch_group_t)、信号量(dispatch_semaphore_t)、屏障(dispatch_barrier_async)等在 OC 和 Swift 中是相同的。底层都是同一个 C 库 (libdispatch)。差异主要体现在语法、API 命名风格以及 Swift 对闭包的现代支持上。


主要差异对比

特性/概念 Objective-C (OC) Swift 说明
队列创建 dispatch_queue_create("com.example.queue", DISPATCH_QUEUE_SERIAL);
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
DispatchQueue(label: "com.example.queue", attributes: .serial)
DispatchQueue.global(qos: .default)
Swift 使用 DispatchQueue 类,更面向对象。QoS 替代了旧的优先级常量。
主队列 dispatch_get_main_queue() DispatchQueue.main Swift 访问主队列更简洁。
异步提交任务 dispatch_async(queue, ^{ ... }); queue.async { ... } Swift 使用实例方法 .async 和尾随闭包,语法更清晰。
同步提交任务 dispatch_sync(queue, ^{ ... }); queue.sync { ... } 同上。
闭包语法 Block: ^{ ... }^returnType (params) { ... } Closure: { ... }{ (params) -> returnType in ... } Swift 闭包语法更现代灵活,支持捕获列表 ([weak self])。
Dispatch Group dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{...});
dispatch_group_notify(group, queue, ^{...});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
let group = DispatchGroup()
queue.async(group: group) { ... }
group.notify(queue: queue) { ... }
group.wait()
Swift 使用 DispatchGroup 类,方法调用更面向对象。async(group:) 是主要提交方式。
Dispatch Semaphore dispatch_semaphore_t sema = dispatch_semaphore_create(1);
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
dispatch_semaphore_signal(sema);
let semaphore = DispatchSemaphore(value: 1)
semaphore.wait()
semaphore.signal()
Swift 使用 DispatchSemaphore 类,方法名更直观。
Dispatch Barrier dispatch_barrier_async(queue, ^{ ... }); queue.async(flags: .barrier) { ... } Swift 使用 .barrier 标志。
线程保活 (RunLoop) 显式创建 NSThread + NSRunLoop + CFRunLoopRun() 显式创建 Thread + RunLoop + RunLoop.run() (或 CFRunLoop) 核心逻辑相同:创建线程 -> 添加 Port/Source -> 启动 RunLoop。OC 常用 NSRunLoop,Swift 常用 RunLoop 或直接调用 CFRunLoopRun
QoS (Quality of Service) DISPATCH_QUEUE_PRIORITY_* (如 DISPATCH_QUEUE_PRIORITY_DEFAULT) .userInteractive, .userInitiated, .default, .utility, .background Swift 的 QoS 枚举更语义化,推荐使用。

常用场景示例对比

场景 1: 后台异步任务 + 主线程更新 UI (最常见)

// Objective-C
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 在后台线程执行耗时操作 (e.g., 网络请求, 图片处理)
    NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
    UIImage *image = [UIImage imageWithData:imageData];

    // 回到主线程更新UI
    dispatch_async(dispatch_get_main_queue(), ^{
        self.imageView.image = image;
    });
});
// Swift
DispatchQueue.global(qos: .userInitiated).async {
    // 在后台线程执行耗时操作
    guard let imageData = try? Data(contentsOf: imageURL),
          let image = UIImage(data: imageData) else { return }

    // 回到主线程更新UI
    DispatchQueue.main.async {
        self.imageView.image = image // 注意: 这里通常需要 [weak self] 避免循环引用
    }
}

关键点:

  • OC: dispatch_async + dispatch_get_global_queue / dispatch_get_main_queue + Block。
  • Swift: .async 方法 + DispatchQueue.global / DispatchQueue.main + Closure。Swift 中强烈建议在闭包内使用 self 时使用捕获列表 [weak self][unowned self] 避免循环引用。

场景 2: 使用 Dispatch Group 管理多个异步任务完成

// Objective-C
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 任务1
dispatch_group_async(group, queue, ^{
    [self downloadTask1];
});

// 任务2
dispatch_group_async(group, queue, ^{
    [self downloadTask2];
});

// 所有任务完成后通知
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"All tasks completed!");
    [self updateUI];
});
// Swift
let group = DispatchGroup()
let queue = DispatchQueue.global(qos: .userInitiated)

// 任务1
queue.async(group: group) {
    self.downloadTask1()
}

// 任务2
queue.async(group: group) {
    self.downloadTask2()
}

// 所有任务完成后通知
group.notify(queue: .main) {
    print("All tasks completed!")
    self.updateUI() // 注意循环引用
}

关键点:

  • OC: dispatch_group_create, dispatch_group_async, dispatch_group_notify
  • Swift: DispatchGroup(), .async(group:) 方法, group.notify(queue:) 方法。逻辑完全一致。

场景 3: 线程保活 (RunLoop)

需求: 创建一个长期存活的后台线程,用于执行一系列需要在同一个线程上顺序处理的任务(例如:数据库操作、日志写入、特定串行网络请求等)。避免线程频繁创建销毁开销。

// Objective-C
// MyThreadManager.h
@property (nonatomic, strong) NSThread *workerThread;

// MyThreadManager.m
- (instancetype)init {
    self = [super init];
    if (self) {
        _workerThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadEntry) object:nil];
        [_workerThread start];
    }
    return self;
}

- (void)threadEntry {
    @autoreleasepool {
        // 1. 给当前RunLoop添加一个Port/Source (保活关键)
        [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
        // 或使用Source: CFRunLoopSourceContext sourceContext = {0}; ... CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);

        // 2. 启动RunLoop (RunLoop会一直运行直到所有Source/Timer被移除或显式停止)
        [[NSRunLoop currentRunLoop] run];
        // 更底层保活(常用): CFRunLoopRun();
    }
}

// 向保活线程提交任务
- (void)performTaskOnWorkerThread:(void (^)(void))task {
    if (!task) return;
    [self performSelector:@selector(executeTask:) onThread:self.workerThread withObject:task waitUntilDone:NO];
}

- (void)executeTask:(void (^)(void))task {
    task(); // 在workerThread上执行传入的block
}
// Swift (使用 Thread 和 RunLoop)
class ThreadManager {
    private var workerThread: Thread?

    init() {
        workerThread = Thread(target: self, selector: #selector(threadEntry), object: nil)
        workerThread?.start()
    }

    @objc private func threadEntry() {
        autoreleasepool {
            // 1. 给当前RunLoop添加一个Port (保活关键)
            RunLoop.current.add(Port(), forMode: .default)
            // 或使用 CFRunLoopSource

            // 2. 启动RunLoop (RunLoop会一直运行直到所有Source/Timer被移除或显式停止)
            RunLoop.current.run()
            // 更底层保活(常用): CFRunLoopRun()
        }
    }

    // 向保活线程提交任务 (使用 performSelector)
    func performTaskOnWorkerThread(task: @escaping () -> Void) {
        guard let workerThread = workerThread else { return }
        perform(#selector(executeTask(_:)), on: workerThread, with: task, waitUntilDone: false)
    }

    @objc private func executeTask(_ task: () -> Void) {
        task() // 在workerThread上执行传入的闭包 (注意: 这里闭包类型需要兼容 @objc)
    }

    // 向保活线程提交任务 (更Swift化的方式 - 需要自定义Source, 较复杂)
    // ... (通常为了简便,performSelector 在保活线程场景仍常用)
}
// Swift (更现代的方式 - 使用 DispatchQueue + RunLoop/Source 混合)
class ThreadManager {
    private let serialQueue = DispatchQueue(label: "com.example.workerThread")
    private var runLoopSource: CFRunLoopSource?

    init() {
        serialQueue.async { [weak self] in
            guard let self = self else { return }
            autoreleasepool {
                // 1. 创建自定义RunLoop Source上下文
                var context = CFRunLoopSourceContext()
                context.perform = { info in
                    // 这里处理被触发的任务 (需要配合队列存储任务)
                    // 例如: if let task = self.taskQueue.dequeue() { task() }
                }

                // 2. 创建Source并添加到当前RunLoop
                let runLoop = CFRunLoopGetCurrent()
                let source = CFRunLoopSourceCreate(nil, 0, &context)
                CFRunLoopAddSource(runLoop, source, .defaultMode)
                self.runLoopSource = source

                // 3. 启动RunLoop
                CFRunLoopRun()
            }
        }
    }

    func performTaskOnWorkerThread(task: @escaping () -> Void) {
        // 1. 将任务放入一个线程安全的队列 (taskQueue)
        // ... (使用 serialQueue.sync 或锁来操作 taskQueue)

        // 2. 唤醒RunLoop处理任务
        if let source = runLoopSource {
            CFRunLoopSourceSignal(source)
            CFRunLoopWakeUp(CFRunLoopGetCurrent()) // 注意: 需要在 serialQueue 上获取当前RunLoop
        }
    }
}

关键点 (线程保活):

  1. 核心原理: RunLoop 在没有 Source/Timer/Observer 时会退出。添加一个永久的 Source (如 Mach Port 或自定义 Source) 是保活的关键。
  2. OC 常用模式: NSThread + [[NSRunLoop currentRunLoop] addPort:...] + [[NSRunLoop currentRunLoop] run]CFRunLoopRun() + performSelector:onThread:... 提交任务。
  3. Swift 常用模式:
    • 类似 OC: Thread + RunLoop.current.add(Port(), ...) + RunLoop.current.run() / CFRunLoopRun() + performSelector:onThread:...。注意闭包兼容 @objc
    • 更 GCD/Swift 风格 (复杂): 在串行 DispatchQueueasync 块中创建自定义 CFRunLoopSource,将其添加到当前 RunLoop 并启动 RunLoop (CFRunLoopRun)。提交任务时,将任务放入队列并 CFRunLoopSourceSignal + CFRunLoopWakeUp 触发 Source 的处理函数来执行队列中的任务。这种方式更底层但更灵活,避免了 performSelector
  4. 保活线程退出: 要停止保活线程,需要在目标线程上调用 CFRunLoopStop(CFRunLoopGetCurrent()) 并移除添加的 Source/Port。管理其生命周期很重要。
  5. 替代方案: 对于大多数需要“常驻”执行串行任务的场景,一个专用的串行 DispatchQueue (DispatchQueue(label: ..., attributes: .serial)) 通常是更简单、更推荐的做法。GCD 会高效地管理底层线程池。显式线程保活主要用于需要严格控制在 特定 线程执行任务(如某些 Core Data 上下文要求)、或需要 RunLoop 特定功能(如 NSTimer 在后台线程调度)的特殊情况。

总结

  1. 核心一致: OC 和 Swift 使用 GCD 的核心逻辑、队列类型、调度操作(async/sync/group/semaphore/barrier)是完全相同的。
  2. 语法差异:
    • OC: 基于 C 函数 (dispatch_async, dispatch_get_global_queue 等) 和 Block (^{...})。
    • Swift: 基于面向对象的类 (DispatchQueue, DispatchGroup, DispatchSemaphore) 和方法调用 (.async { ... }) 以及更现代的闭包语法。API 命名更 Swifty。
  3. 线程保活: 原理相同(RunLoop + Source)。OC 常用 NSThread/NSRunLoop + performSelector:onThread:。Swift 可选择类似 OC 的方式(注意 @objc)或更底层地使用 DispatchQueue + CFRunLoopSource 的方式。现代开发中,优先考虑使用专用串行 DispatchQueue 代替显式线程保活,除非有特殊需求。
  4. Swift 优势: 语法更简洁安全(闭包捕获列表),QoS 更语义化,API 设计更一致。
  5. 内存管理: Swift 中闭包捕获 self 时需特别注意循环引用,使用 [weak self][unowned self]。OC Block 同样需要注意循环引用(__weak typeof(self) weakSelf = self;)。

选择 OC 还是 Swift 编写 GCD 代码主要取决于项目语言本身。理解 GCD 的核心概念和 RunLoop 的保活机制是基础,语法差异相对容易适应。在 Swift 中,尽量使用其更现代、安全的语法特性。

相关文章

  • 多线程

    参考文章:iOS多线程--彻底学会多线程之『GCD』Swift 3.0 GCD和DispatchQueue 使用解...

  • iOS多线程NSThread/GCD/NSOperation区别

    iOS多线程NSThread/GCD/NSOperation区别和使用

  • swift多线程

    swift 2.0 和 3.0的多线程的区别 swift 2.0多线程的基础使用 dispatch_async(d...

  • swift3多线程之GCD

    多线程可以说ios进阶高级程序员的必修课,swift2的时候GCD还是继承的OC中多线程的C API,在swift...

  • iOS 10.17日记

    swift 学习 1 static和class的区别 2 学习手势的使用 3 swift 中kvo的使用(和oc...

  • Swift基础语法-if语句--对比OC

    if语句基本使用 OC和Swift中的区别 OC 中如果只有一条指令if后面的大括号可以省略 Swift: if ...

  • Swift3使用GCD和DispatchQueues关于gcd

    关于对gcd 的使用,有几个链接写的还是相当不错的。Swift3使用GCD和DispatchQueueshttp:...

  • 高级iOS面试题全纪录

    iOS基础: 多线程使用,gcd跟operation区别,怎么取消正在执行的gcd任务 GCD 系列知识总结 NS...

  • iOS多线程

    OC中的多线程 OC中多线程根据封装程度可以分为三个:NSThread、GCD和NSOperation,本文主要讲...

  • iOS多线程编程

    OC中的多线程 OC中多线程根据封装程度可以分为三个层次:NSThread、GCD和NSOperation,另外由...

网友评论

      本文标题:OC 和 Swift 关于GCD多线程的使用区别

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