使用 NSOperationQueue 处理复杂任务
探索 NSOperation 和 NSOperationQueue,以更有条理且易于管理的方式执行并发任务,包括处理任务依赖关系。
使用 NSOperationQueue 处理复杂任务 是 CoddyKit 上的免费 Objective-C iOS Development for Legacy & Enterprise Apps 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Objective-C iOS Development for Legacy & Enterprise Apps 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Objective-C iOS Development for Legacy & Enterprise Apps 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
Why NSOperationQueue?
You've learned about Grand Central Dispatch (GCD) for concurrent tasks. While powerful, GCD can become complex for intricate task management.
NSOperationQueue offers a higher-level, object-oriented way to handle concurrency. It builds on top of GCD, providing more control and features like dependencies, cancellation, and state management.
What is NSOperation?
At the core of NSOperationQueue is NSOperation. It's an abstract class representing a single unit of work that can be executed concurrently.
You don't instantiate NSOperation directly. Instead, you use its concrete subclasses or create your own custom subclasses to define specific tasks.
- Encapsulates work: Each operation is a self-contained task.
- Manages state: Operations have states like "ready," "executing," "finished."
- Supports dependencies: Operations can depend on others completing first.
Build Your Own Operation
To create a custom operation, you subclass NSOperation and override its main method. This is where your task's logic goes.
Remember to mark your operation as "finished" when done, especially for asynchronous operations. For synchronous operations, main handles it.
Try running this simple custom operation:
#import <Foundation/Foundation.h>
// CustomOperation.h
@interface CustomOperation : NSOperation
@end
// CustomOperation.m
@implementation CustomOperation
- (void)main {
if (self.isCancelled) {
return; // Always check for cancellation
}
NSLog(@"Custom operation started!");
// Simulate some work
[NSThread sleepForTimeInterval:1.0];
NSLog(@"Custom operation finished!");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
CustomOperation *op = [[CustomOperation alloc] init];
[op start]; // Manually start for demo
}
return 0;
}Simple Tasks with BlockOperation
For simpler tasks that don't require a custom subclass, NSBlockOperation is perfect. It allows you to wrap one or more blocks of code as an operation.
This is often used for quick, one-off tasks without the overhead of creating a new class.
Here's how to use it:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"Block operation running!");
[NSThread sleepForTimeInterval:0.5];
NSLog(@"Block operation done!");
}];
[blockOp start]; // Manually start for demo
}
return 0;
}Executing with NSOperationQueue
To truly leverage concurrency, you add operations to an NSOperationQueue. The queue manages the execution of operations, often concurrently.
The queue decides when to start operations based on their readiness and dependencies. It handles the threading for you.
Observe how both operations run concurrently:
#import <Foundation/Foundation.h>
@interface CustomOperation : NSOperation
@end
@implementation CustomOperation
- (void)main {
if (self.isCancelled) return;
NSLog(@"Custom operation started!");
[NSThread sleepForTimeInterval:1.0];
NSLog(@"Custom operation finished!");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
CustomOperation *op1 = [[CustomOperation alloc] init];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"Block operation started!");
[NSThread sleepForTimeInterval:0.5];
NSLog(@"Block operation finished!");
}];
[queue addOperation:op1];
[queue addOperation:op2];
// Wait for all operations to complete
[queue waitUntilAllOperationsAreFinished];
NSLog(@"All operations completed!");
}
return 0;
}Chaining Tasks with Dependencies
One of NSOperation's most powerful features is setting dependencies. An operation can be configured to only start after another specific operation (or set of operations) has finished.
This is crucial for tasks that must be executed in a particular order, ensuring data consistency or proper workflow.
Notice how "Op B" waits for "Op A":
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *opA = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"Operation A running...");
[NSThread sleepForTimeInterval:1.0];
NSLog(@"Operation A done!");
}];
NSBlockOperation *opB = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"Operation B running...");
[NSThread sleepForTimeInterval:0.5];
NSLog(@"Operation B done!");
}];
// Op B depends on Op A
[opB addDependency:opA];
[queue addOperation:opA];
[queue addOperation:opB];
[queue waitUntilAllOperationsAreFinished];
NSLog(@"All dependent operations completed!");
}
return 0;
}Stopping Tasks Gracefully
Operations can be cancelled, which is vital for user-initiated stops or when a task becomes irrelevant.
When an operation is cancelled, its isCancelled property becomes YES. Your main method (or block) should regularly check this property and exit gracefully if it's true.
An operation that has already started might still complete some work before checking isCancelled.
#import <Foundation/Foundation.h>
@interface CancellableOperation : NSOperation
@end
@implementation CancellableOperation
- (void)main {
for (int i = 0; i < 5; i++) {
if (self.isCancelled) {
NSLog(@"Operation was cancelled early!");
return;
}
NSLog(@"Working... %d", i);
[NSThread sleepForTimeInterval:0.2];
}
NSLog(@"Operation finished normally.");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
CancellableOperation *op = [[CancellableOperation alloc] init];
[queue addOperation:op];
// Wait a short time then try to cancel
[NSThread sleepForTimeInterval:0.3];
[op cancel];
NSLog(@"Attempted to cancel operation.");
[queue waitUntilAllOperationsAreFinished];
NSLog(@"Queue finished.");
}
return 0;
}Post-Execution Actions
Sometimes you need to perform an action only after an operation has finished, regardless of whether it succeeded or was cancelled.
Every NSOperation has a completionBlock property. This block is executed on an arbitrary thread once the operation's main method (or blocks for NSBlockOperation) has completed.
Use it for cleanup or to update the UI (remember to dispatch UI updates to the main thread!).
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"Operation running...");
[NSThread sleepForTimeInterval:0.5];
}];
op.completionBlock = ^{
NSLog(@"Operation completion block executed!");
// If updating UI, dispatch to main thread:
// dispatch_async(dispatch_get_main_queue(), ^{ /* UI update */ });
};
[queue addOperation:op];
[queue waitUntilAllOperationsAreFinished];
NSLog(@"All done!");
}
return 0;
}Managing Parallelism
An NSOperationQueue can execute multiple operations concurrently. You can control this behavior using the maxConcurrentOperationCount property.
1: Operations run serially (one at a time).NSOperationQueueDefaultMaxConcurrentOperationCount(usually -1): The queue decides the optimal number based on system conditions.- Any positive integer: Limits the number of operations running simultaneously.
This helps prevent resource exhaustion and manage workload.
Check Your Understanding
Test your knowledge on NSOperation and NSOperationQueue.
NSOperationQueue Recap
Great job! You've explored NSOperation and NSOperationQueue.
- NSOperation represents a single unit of work.
- NSOperationQueue manages the execution of operations, providing a higher-level abstraction over GCD.
- Key features include dependencies, cancellation, completion blocks, and control over concurrency.
- Use
NSBlockOperationfor simple tasks and custom subclasses for complex, reusable operations.
This powerful framework helps you build robust and manageable concurrent applications.
常见问题解答
「使用 NSOperationQueue 处理复杂任务」课时是免费的吗?
是的 — 「使用 NSOperationQueue 处理复杂任务」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Objective-C iOS Development for Legacy & Enterprise Apps 课程的其余内容,请升级到 CoddyKit PRO。 Objective-C iOS Development for Legacy & Enterprise Apps 课程共包含 4 节课。
「使用 NSOperationQueue 处理复杂任务」这节课中我会学到什么?
探索 NSOperation 和 NSOperationQueue,以更有条理且易于管理的方式执行并发任务,包括处理任务依赖关系。 你通过在浏览器中直接运行的动手代码来练习 Objective-C iOS Development for Legacy & Enterprise Apps,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Objective-C iOS Development for Legacy & Enterprise Apps 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Objective-C iOS Development for Legacy & Enterprise Apps 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「使用 NSOperationQueue 处理复杂任务」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Objective-C iOS Development for Legacy & Enterprise Apps 课中编写并运行代码吗?
能。每节 Objective-C iOS Development for Legacy & Enterprise Apps 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 使用 NSURLSession 调用 API
- Grand Central Dispatch(GCD)
- 使用 NSOperationQueue 处理复杂任务
- 使用 NSJSONSerialization 解析 JSON