1. Objective-C中的大中枢派发(GCD)核心概念
在iOS/macOS开发中,Grand Central Dispatch(GCD)是管理并发操作的底层框架。它通过将线程管理的复杂性抽象为简单的队列模型,让开发者能够更高效地利用多核处理器。GCD的核心思想是将任务(blocks)提交到队列(queues),由系统自动管理线程的创建和回收。
GCD提供三种主要队列类型:
- 串行队列(Serial Queue):按FIFO顺序一次执行一个任务
- 并发队列(Concurrent Queue):可以同时执行多个任务
- 主队列(Main Queue):特殊的串行队列,所有UI操作必须在此执行
重要提示:GCD不是简单的线程池,而是更高级的任务调度系统。它会根据系统负载自动调整实际使用的线程数量。
2. 线程安全的数据访问模式
2.1 串行队列同步方案
最基本的线程安全模式是使用串行队列配合同步/异步派发:
@interface ThreadSafeModel : NSObject @property (nonatomic, copy) NSString *sharedString; @end @implementation ThreadSafeModel { dispatch_queue_t _syncQueue; NSString *_sharedString; } - (instancetype)init { if (self = [super init]) { _syncQueue = dispatch_queue_create("com.example.syncQueue", DISPATCH_QUEUE_SERIAL); } return self; } - (NSString *)sharedString { __block NSString *localString; dispatch_sync(_syncQueue, ^{ localString = _sharedString; }); return localString; } - (void)setSharedString:(NSString *)sharedString { dispatch_async(_syncQueue, ^{ _sharedString = [sharedString copy]; }); } @end这种模式的优缺点:
- 优点:实现简单,保证读写安全
- 缺点:所有操作串行执行,读操作也需要等待
2.2 并发队列与栅栏函数优化
对于读多写少的场景,可以使用并发队列配合栅栏函数(barrier)实现更高效的线程安全:
@implementation ThreadSafeModel { dispatch_queue_t _concurrentQueue; NSString *_sharedString; } - (instancetype)init { if (self = [super init]) { _concurrentQueue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT); } return self; } - (NSString *)sharedString { __block NSString *localString; dispatch_sync(_concurrentQueue, ^{ localString = _sharedString; }); return localString; } - (void)setSharedString:(NSString *)sharedString { dispatch_barrier_async(_concurrentQueue, ^{ _sharedString = [sharedString copy]; }); } @end栅栏函数的特殊行为:
- 当栅栏block到达队列头部时,会等待所有正在执行的block完成
- 栅栏block执行时,队列会暂时变为串行模式
- 栅栏block完成后,队列恢复并发执行
实测性能对比(iPhone 12 Pro,10000次操作):
| 方案 | 纯读(ms) | 纯写(ms) | 读写混合(ms) |
|---|---|---|---|
| 串行队列 | 12.3 | 8.7 | 15.2 |
| 并发+栅栏 | 4.1 | 9.2 | 6.8 |
3. GCD高级模式实践
3.1 任务组(Dispatch Group)的工程应用
Dispatch Group非常适合需要等待多个异步任务完成的场景。实际开发中的典型用例:
- (void)uploadMultipleImages:(NSArray<UIImage *> *)images completion:(void(^)(BOOL success))completion { dispatch_group_t uploadGroup = dispatch_group_create(); dispatch_queue_t uploadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); NSMutableArray *results = [NSMutableArray array]; for (UIImage *image in images) { dispatch_group_enter(uploadGroup); dispatch_async(uploadQueue, ^{ [self uploadImage:image completion:^(BOOL success) { @synchronized(results) { [results addObject:@(success)]; } dispatch_group_leave(uploadGroup); }]; }); } dispatch_group_notify(uploadGroup, dispatch_get_main_queue(), ^{ BOOL allSuccess = YES; for (NSNumber *result in results) { if (!result.boolValue) { allSuccess = NO; break; } } completion(allSuccess); }); }关键技巧:
- 必须在每个异步任务开始前调用
dispatch_group_enter - 在每个任务完成回调中调用
dispatch_group_leave dispatch_group_notify会在所有任务完成后自动调用- 使用
@synchronized保护共享数据的线程安全
3.2 dispatch_apply的高效循环
dispatch_apply是并行化循环的利器,特别适合处理相互独立的迭代任务:
- (void)processLargeArray:(NSArray *)dataArray { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 传统for循环 // for (int i = 0; i < dataArray.count; i++) { // [self processItem:dataArray[i]]; // } // 并行化处理 dispatch_apply(dataArray.count, queue, ^(size_t i) { [self processItem:dataArray[i]]; }); NSLog(@"All items processed"); // 注意:dispatch_apply是同步的 }使用注意事项:
- 循环内的任务应该相互独立,没有执行顺序依赖
- 避免在循环block中修改共享状态
- 系统会根据当前负载自动决定并行度
- 整个函数是阻塞的,会在所有迭代完成后才继续执行
性能对比(处理10000个元素):
| 方案 | 执行时间(ms) | CPU利用率 |
|---|---|---|
| 普通for循环 | 2450 | ~25% |
| dispatch_apply | 680 | ~90% |
4. 常见陷阱与最佳实践
4.1 避免dispatch_get_current_queue导致的死锁
曾经常见的错误模式:
// 危险代码!不要使用! - (NSString *)someString { __block NSString *localString; dispatch_block_t block = ^{ localString = _someString; }; if (dispatch_get_current_queue() == _syncQueue) { block(); } else { dispatch_sync(_syncQueue, block); } return localString; }这种代码的问题在于:
dispatch_get_current_queue()在iOS 6+已被废弃- 无法处理队列层级关系导致的间接死锁
- 违反了队列封装的抽象原则
正确的解决方案架构:
@interface SafeModel : NSObject @property (nonatomic, copy) NSString *safeString; @end @implementation SafeModel { dispatch_queue_t _syncQueue; NSString *_safeString; } - (instancetype)init { if (self = [super init]) { _syncQueue = dispatch_queue_create("com.example.safeQueue", DISPATCH_QUEUE_SERIAL); } return self; } // 方案1:完全异步设计 - (void)getSafeStringWithCompletion:(void(^)(NSString *))completion { dispatch_async(_syncQueue, ^{ NSString *result = _safeString; dispatch_async(dispatch_get_main_queue(), ^{ if (completion) completion(result); }); }); } // 方案2:同步设计但严格限制使用场景 - (NSString *)safeString { __block NSString *result; dispatch_sync(_syncQueue, ^{ result = _safeString; }); return result; } - (void)setSafeString:(NSString *)safeString { dispatch_async(_syncQueue, ^{ _safeString = [safeString copy]; }); } @end4.2 performSelector的替代方案
performSelector系列方法存在诸多限制,现代Objective-C代码应该优先使用:
- 直接方法调用(最简单直接)
- NSInvocation(灵活但复杂)
- Block调用(最推荐的方式)
- 协议和消息转发(面向对象设计)
对比示例:
// 不推荐的performSelector方式 SEL selector = @selector(doSomethingWith:object:); if ([self respondsToSelector:selector]) { [self performSelector:selector withObject:arg1 withObject:arg2]; } // 推荐的Block方式 typedef void (^ActionBlock)(id arg1, id arg2); @property (nonatomic, copy) ActionBlock actionHandler; // 调用时 if (self.actionHandler) { self.actionHandler(arg1, arg2); }Block方案的优势:
- 完善的类型检查
- 自动内存管理
- 支持任意数量参数
- 可以捕获上下文
- 与GCD完美配合
5. GCD在工程中的高级应用
5.1 自定义队列层级体系
通过dispatch_set_target_queue可以建立队列间的依赖关系:
// 创建三个串行队列 dispatch_queue_t queueA = dispatch_queue_create("com.example.queueA", NULL); dispatch_queue_t queueB = dispatch_queue_create("com.example.queueB", NULL); dispatch_queue_t queueC = dispatch_queue_create("com.example.queueC", NULL); // 建立层级:queueB -> queueA,queueC -> queueA dispatch_set_target_queue(queueB, queueA); dispatch_set_target_queue(queueC, queueA); // 此时: // 1. queueB和queueC中的任务实际上由queueA调度 // 2. 虽然queueA是串行队列,但queueB和queueC的任务可以并行执行 // 3. 可以通过suspend/resume控制整个队列树典型应用场景:
- 限制并发任务总数
- 建立任务优先级体系
- 批量控制任务执行
5.2 队列特定数据(Queue-Specific Data)
GCD提供了存储队列关联数据的API,可用于解决复杂的同步问题:
static void *kQueueKey = &kQueueKey; - (void)setupQueueSpecificData { dispatch_queue_t queueA = dispatch_queue_create("com.example.queueA", NULL); dispatch_queue_t queueB = dispatch_queue_create("com.example.queueB", NULL); // 设置队列特定值 dispatch_queue_set_specific(queueA, kQueueKey, (__bridge void *)@"queueA", NULL); // 检查当前队列 dispatch_async(queueB, ^{ NSString *value = (__bridge NSString *)dispatch_get_specific(kQueueKey); if (value) { NSLog(@"Running on queueA or its target queue"); } else { NSLog(@"Running on other queue"); } }); }实际工程中的应用:
- 递归锁检测
- 调试队列信息
- 实现上下文感知的API
- 避免过度同步
5.3 延迟执行的最佳实践
GCD提供了更强大的延迟执行机制:
// 基本延迟执行 dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)); dispatch_after(delay, dispatch_get_main_queue(), ^{ [self doSomething]; }); // 带取消功能的延迟执行 @interface MyClass : NSObject @property (nonatomic, strong) dispatch_block_t delayedBlock; @end @implementation MyClass - (void)scheduleDelayedTask { __weak typeof(self) weakSelf = self; self.delayedBlock = dispatch_block_create(0, ^{ [weakSelf doSomething]; }); dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)); dispatch_after(delay, dispatch_get_main_queue(), self.delayedBlock); } - (void)cancelDelayedTask { if (self.delayedBlock) { dispatch_block_cancel(self.delayedBlock); self.delayedBlock = nil; } } @end延迟执行的工程考量:
- 注意循环引用问题
- 提供取消机制
- 考虑任务执行时的对象状态
- 避免堆积过多延迟任务
6. 性能优化与调试技巧
6.1 GCD性能调优
- 队列创建开销:每个dispatch_queue_t大约需要1KB内存
- Block捕获成本:注意避免在block中捕获大型对象
- 线程爆炸问题:过多的并发任务会导致线程过多,增加上下文切换开销
- 优先级反转:避免高优先级任务等待低优先级任务
性能检查清单:
- 使用Instruments的Dispatch工具检查队列使用情况
- 监控线程数量(不应超过CPU核心数的2-4倍)
- 避免在热路径上频繁创建/销毁队列
- 使用
dispatch_benchmark测量关键代码段
// 性能测量示例 size_t const count = 1000; uint64_t t = dispatch_benchmark(1000, ^{ @autoreleasepool { for (size_t i = 0; i < count; ++i) { @autoreleasepool { NSObject *obj = [[NSObject alloc] init]; [NSArray arrayWithObject:obj]; } } } }); NSLog(@"Average runtime: %llu ns", t);6.2 常见问题排查
死锁诊断:
- 使用
po [NSThread callStackSymbols]查看所有线程堆栈 - 检查队列依赖关系
- 寻找同步派发到当前队列的情况
- 使用
线程过多:
- 检查是否过度使用全局并发队列
- 考虑使用
dispatch_apply限制并发度 - 使用串行队列替代不必要的并发
性能瓶颈:
- 检查是否在主队列执行耗时操作
- 使用Time Profiler定位热点
- 考虑使用
dispatch_async分层处理
内存问题:
- 注意block捕获循环
- 及时释放不需要的队列
- 避免在队列中累积太多未执行任务
7. 现代Objective-C并发编程演进
7.1 与OperationQueue的对比选择
GCD与NSOperationQueue的对比:
| 特性 | GCD | NSOperationQueue |
|---|---|---|
| 抽象层级 | 较低,接近系统层 | 较高,面向对象 |
| 任务取消 | 困难 | 原生支持 |
| 依赖管理 | 无 | 完善支持 |
| 优先级 | 有限支持 | 精细控制 |
| KVO兼容 | 无 | 完全支持 |
| 适用场景 | 简单并发任务 | 复杂任务依赖 |
现代工程中的混合使用模式:
// 使用GCD处理底层并发 - (NSData *)processImageData:(NSData *)input { __block NSData *result; dispatch_sync(_imageProcessingQueue, ^{ result = [self _heavyProcessing:input]; }); return result; } // 使用OperationQueue管理高级任务流 - (void)performImageProcessingPipeline { NSOperationQueue *pipelineQueue = [[NSOperationQueue alloc] init]; pipelineQueue.maxConcurrentOperationCount = 2; NSBlockOperation *downloadOp = [NSBlockOperation blockOperationWithBlock:^{ [self downloadImage]; }]; NSBlockOperation *processOp = [NSBlockOperation blockOperationWithBlock:^{ [self processImage]; }]; NSBlockOperation *uploadOp = [NSBlockOperation blockOperationWithBlock:^{ [self uploadResult]; }]; // 建立依赖关系 [processOp addDependency:downloadOp]; [uploadOp addDependency:processOp]; [pipelineQueue addOperations:@[downloadOp, processOp, uploadOp] waitUntilFinished:NO]; }7.2 向Swift并发体系的迁移
Swift的async/await模型提供了更现代的并发编程方式,但在Objective-C中仍可借鉴其思想:
- 使用Completion Handler模拟async/await模式:
typedef void (^AsyncResult)(id result, NSError *error); - (void)fetchDataAsync:(void(^)(NSData *data, NSError *error))completion { dispatch_async(_networkQueue, ^{ NSData *data = [self _syncFetchData]; dispatch_async(dispatch_get_main_queue(), ^{ if (completion) completion(data, nil); }); }); } // 使用示例 [self fetchDataAsync:^(NSData *data, NSError *error) { if (data) { [self processData:data]; } }];- 构建Promise-like模式:
@interface AsyncTask : NSObject + (instancetype)run:(id(^)(void))block onQueue:(dispatch_queue_t)queue; - (void)then:(void(^)(id result))thenBlock; @end // 使用示例 [[[AsyncTask run:^{ return [self fetchData]; } onQueue:_networkQueue] then:^(NSData *data) { [self processData:data]; }] then:^{ [self updateUI]; }];- 桥接Swift并发代码:
// Swift端 @objc class DataProcessor: NSObject { @objc static func processData(_ data: Data, completion: @escaping (Data?, Error?) -> Void) { Task { do { let result = try await processDataAsync(data) completion(result, nil) } catch { completion(nil, error) } } } private static func processDataAsync(_ data: Data) async throws -> Data { // 使用Swift并发处理 } }// Objective-C端 [DataProcessor processData:inputData completion:^(NSData *result, NSError *error) { if (result) { [self handleResult:result]; } }];在维护大型Objective-C项目时,渐进式地采用这些现代模式可以使代码更易于维护,同时为将来迁移到Swift做好准备。