NSOperation

最近做了一些和 NSOperation 有关的事情,感觉这个类还是挺有意思的,这里记录一下细节和遇到的一些坑。

我们一般使用 NSOperation 的时候,通常会配合 NSOperationQueue 来进行管理,通过配置 NSOperationQueue 的 maxConcurrentOperationCount 来设置为并行或者串行。而对于一个 NSOperation 来说,我们也可把它处理成同步或异步的,针对不同的情况,我们也需要做不同的处理。

我们打开 NSOperation.h 会首先看到 start 方法和 main 方法。接下来我就详细说下自己的理解。

Concurrency Programming Guide

main 方法

看文档的注释发现,NSOperation 的默认实现是什么都没做,也就是个空方法,当子类去重写的时候,也不用调 super,而且它会在一个由 NSOperation 提供的 autorelease pool 中执行,子类也不需要创建自己的 autorelease pool

当我们实现一个同步的方法的时候,我们只需要重写 main 函数,处理我们的业务逻辑即可,不需要处理除 canceled 之外的任何状态。关于 canceled,后边再说。

start 方法

这个方法默认实现是处理了一些状态,检查这个 operation 是否可以正常执行,如 ready 是否为 YES, cancelled 是否为 YES, finished 是否为 YES ,然后调用 main 方法。如果不满足条件,直接 return,就结束了。

如果要执行一个异步的 Operation,例如发送一个网络请求,那么,需要重写 start 方法,并自己手动设置一些状态(不能调用 super),处理这些状态的时候,需要手动触发一些 KVO 来通知其他可能监听的对象,如 isCancelled, isExecuting, isFinished 等。当异步处理结束时,需要更新 finished 和 executing 状态。

如果是异步的话,我们也应该在 处理 asynchronous 属性,返回 YES

cancelled

我们应该一直关注 canceled 状态,当被标记为 YES 的时候,我们需要取消我们现在所执行的任务,而 不只是startmain 函数中对 canceled 状态进行判断。当有一个循环,或者说是耗时比较长的操作中,我们应该在比较好处理的位置,判断 canceled 状态,在 YES 的情况下,结束我们的 operation。

  1. 如果是 YES 的话,应该立即结束
  2. 在每个循环中,至少判断一次,如果一个迭代比较耗时,建议多检查几次
  3. 在任何处理比较方便的位置,取消任务

说了那么多,那我们看看如何定义一个子类,来处理同步和异步

同步

1
2
3
4
// MySyncOperation.h
@interface MySyncOperation : NSOperation

@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// MySyncOperation.m
@implementation MySyncOperation

- (void)main {
// 处理自己的业务逻辑
// 注意处理 cancelled
}

- (BOOL)isAsynchronous {
return NO;
}

@end

异步

1
2
3
4
// MyASyncOperation.h
@interface MyAsyncOperation : NSOperation

@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// MyASyncOperation.m
@interface MyAsyncOperation ()
{
BOOL executing;
BOOL finished;
BOOL cancelled;
}

@end

@implementation MyAsyncOperation

// 只需重写 start 方法,不能重写 main。
- (void)start {
if (!self.cancelled) {
[self setOperationToExecutingState];
[self handleBusiness];
} else {
[self setOperationToFinishState];
}
}

- (void)cancel {
if (self.isFinished) {
return;
}
[super cancel];
[self setOperationToCancelState];
}

- (void)handleBusiness {
// 处理自己的业务逻辑
// 注意处理 cancelled
// 在异步处理结束的时候调用 [self setOperationToFinishState] 来结束 operation,耗时操作中,注意处理 cancel

}

- (void)setOperationToExecutingState {
[self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"];
executing = YES;
finished = NO;
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
}

- (void)setOperationToFinishState {
[self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"];
executing = NO;
finished = YES;
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
}

- (void)setOperationToCancelState {
[self willChangeValueForKey:@"isCancelled"];
cancelled = YES;
[self didChangeValueForKey:@"isCancelled"];
}

- (BOOL)isExecuting {
return executing;
}

- (BOOL)isFinished {
return finished;
}

- (BOOL)isCancelled {
return cancelled;
}

- (BOOL)isAsynchronous {
return YES;
}
@end