深入理解GrandCentralDispatch与后台处理
立即解锁
发布时间: 2025-08-20 01:38:24 阅读量: 1 订阅数: 6 


iOS 7开发入门:探索iOS SDK
### 深入理解 Grand Central Dispatch 与后台处理
#### 1. 主线程与工作单元
在 iOS 应用中,默认情况下,所有操作都在主线程中进行,比如处理用户事件触发的操作。对于简单应用来说,这无需过多担忧,因为用户触发的操作方法本身就在主线程中运行。
不过,编写无错误的多线程代码对于普通程序员而言几乎是不可能的。在多线程间同步数据和操作时,代码中需要处理的复杂交互超出了大多数人的能力范围。
幸运的是,我们可以借助软件抽象来实现并发,而无需过多地直接操作线程。苹果推荐的解决方案是将长时间运行的任务拆分为工作单元,并将这些单元放入队列中执行。系统会为我们管理队列,在多个线程上执行工作单元,我们无需直接启动和管理后台线程,系统会处理大部分多线程应用实现中涉及的记录工作。
#### 2. GCD:低级别队列
将工作单元放入可在后台执行的队列,由系统管理线程,这个概念非常强大,大大简化了许多需要并发的开发场景。GCD 几年前首次在 OS X 上推出,为实现这一功能提供了基础设施,随后也应用到了 iOS 平台。它不仅适用于 Objective-C,还适用于 C 和 C++。
GCD 将工作单元、轻松的后台处理和自动线程管理等优秀概念融入到一个 C 接口中,所有基于 C 的语言都可以使用。此外,苹果已将 GCD 的实现开源,因此它也可以移植到其他类 Unix 操作系统。
GCD 的一个关键概念是队列。系统提供了一些预定义的队列,包括一个保证始终在主线程上工作的队列,这对于非线程安全的 UIKit 非常合适。我们也可以根据需要创建自己的队列。GCD 队列严格遵循先进先出(FIFO)原则,添加到 GCD 队列的工作单元将按照它们在队列中的顺序开始执行,但不一定按相同顺序完成,因为 GCD 队列会在可能的情况下自动将工作分配到多个线程上。
GCD 可以访问一个线程池,这些线程在应用程序的生命周期内会被重复使用,并且它会尝试维护适合机器架构的线程数量。当有工作要做时,它会自动利用更强大的机器,使用更多的处理器核心。
#### 3. 块(Blocks)的使用
苹果为 C 语言(以及 Objective-C 和 C++)添加了新的语法,实现了一种名为块(也称为闭包或 lambda)的语言特性,这对于充分利用 GCD 非常重要。块的概念是让一段特定的代码可以像其他 C 语言类型一样处理。块可以赋值给变量、作为参数传递给函数或方法,并且可以执行。
块可以像方法或函数一样接受一个或多个参数并指定返回值。声明块变量时,使用脱字符(^)符号以及一些额外的括号来声明参数和返回类型。定义块本身时,大致相同,但后面要跟上用花括号包裹的实际代码。
以下是一个简单的块示例:
```objc
// Declare a block variable "loggerBlock" with no parameters
// and no return value.
void (^loggerBlock)(void);
// Assign a block to the variable declared above. A block without parameters
// and with no return value, like this one, needs no "decorations" like the use
// of void in the preceding variable declaration.
loggerBlock = ^{ NSLog(@"I’m just glad they didn’t call it a lambda"); };
// Execute the block, just like calling a function.
loggerBlock(); // this produces some output in the console
```
块与 C 语言中的函数指针概念类似,但有一些关键区别。最大的区别之一是块可以在代码中内联定义,并且可以访问其创建范围内的所有变量。默认情况下,块会“捕获”它访问的任何变量,将值复制到一个同名的新变量中,保持原始变量不变。对于 Objective-C 对象,会自动发送 retain 消息,执行完后发送 release 消息;对于标量值(如 int 和 float),则只是简单复制。
如果希望块能够修改外部变量,可以在变量声明前加上 __block 存储限定符;如果希望传递具有弱引用语义的对象指针,可以在前面加上 __weak。
以下是一个使用 __block 的示例:
```objc
// define a variable that can be changed by a block
__block int a = 0;
// define a block that tries to modify a variable in its scope
void (^sillyBlock)(void) = ^{ a = 47; };
// check the value of our variable before calling the block
NSLog(@"a == %d", a); // outputs "a == 0"
// execute the block
sillyBlock();
// check the values of our variable again, after calling the block
NSLog(@"a == %d", a); // outputs "a == 47"
```
块与 GCD 结合使用时效果最佳,我们可以将一个块一步添加到队列中。当直接定义块并添加到队列时,我们可以直接在使用的上下文中看到相关代码。
#### 4. 优化 SlowWorker 的 doWork 方法
为了说明块的工作原理,我们来看一下 SlowWorker 的 doWork: 方法。原始的 doWork: 方法如下:
```objc
- (IBAction)doWork:(id)sender
{
NSDate *startTime = [NSDate date];
NSString *fetchedData = [self fetchSomethingFromServer];
NSString *processedData = [self processData:fetchedData];
NSString *firstResult = [self calculateFirstResult:processedData];
NSString *secondResult = [self calculateSecondResult:processedData];
NSString *resultsSummary = [NSString stringWithFormat:
@"First: [%@]\nSecond: [%@]", firstResult,
secondResult];
self.resultsTextView.text = resultsSummary;
NSDate *endTime = [NSDate date];
NSLog(@"Completed in %f seconds",
[endTime timeIntervalSinceDate:startTime]);
}
```
我们可以通过将所有代码包装在一个块中,并将其传递给 GCD 的 dispatch_async 函数,使该方法完全在后台运行。dispatch_async 函数接受两个参数:一个 GCD 队列和一个要分配给该队列的块。
修改后的 doWork: 方法如下:
```objc
- (IBAction)doWork:(id)sender
{
NSDate *startTime = [NSDate date];
dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSString *fetchedData = [self fetchSomethingFromServer];
NSString *processedData = [self processData:fetchedData];
NSString *firstResult = [self calculateFirstResult:processedData];
NSString *secondResult = [self calculateSecondResult:processedData];
NSString *resultsSummary = [NSString stringWithFormat:
@"First: [%@]\nSecond: [%@]", firstResult,
secondResult];
self.resultsTextView.text = resultsSummary;
NSDate *endTime = [NSDate date];
NSLog(@"Completed in %f seconds",
[endTime timeIntervalSinceDate:startTime]);
});
}
```
这里,我们使用 dispa
0
0
复制全文
相关推荐




