forked from SDWebImage/SDWebImage
/
LIFOOperationQueue.m
executable file
·162 lines (121 loc) · 3.43 KB
/
LIFOOperationQueue.m
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//
// LIFOOperationQueue.m
//
// Created by Ben Harris on 8/19/12.
//
#import "LIFOOperationQueue.h"
@interface LIFOOperationQueue ()
@property (nonatomic, strong) NSMutableArray *runningOperations;
- (void)startNextOperation;
- (void)startOperation:(NSOperation *)op;
@end
@implementation LIFOOperationQueue
@synthesize maxConcurrentOperationCount;
@synthesize operations;
@synthesize runningOperations;
#pragma mark - Initialization
- (id)init {
self = [super init];
if (self) {
self.operations = [NSMutableArray array];
self.runningOperations = [NSMutableArray array];
}
return self;
}
- (id)initWithMaxConcurrentOperationCount:(int)maxOps {
self = [self init];
if (self) {
self.maxConcurrentOperationCount = maxOps;
}
return self;
}
#pragma mark - Operation Management
//
// Adds an operation to the front of the queue
// Also starts operation on an open thread if possible
//
- (void)addOperation:(NSOperation *)op {
if ( [self.operations containsObject:op] )
{
if (!op.isExecuting)
{
[self.operations removeObject:op];
[self.operations insertObject:op atIndex:0];
}
}
else
[self.operations insertObject:op atIndex:0];
if ( (int)self.runningOperations.count < self.maxConcurrentOperationCount ) {
[self startNextOperation];
}
}
//
// Helper method that creates an NSBlockOperation and adds to the queue
//
- (void)addOperationWithBlock:(void (^)(void))block {
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:block];
[self addOperation:op];
}
//
// Attempts to cancel all operations
//
- (void)cancelAllOperations {
self.operations = [NSMutableArray array];
for (int i = 0; i < (int)self.runningOperations.count; i++) {
NSOperation *runningOp = [self.runningOperations objectAtIndex:i];
[runningOp cancel];
[self.runningOperations removeObject:runningOp];
i--;
}
}
#pragma mark - Running Operations
//
// Finds next operation and starts on first open thread
//
- (void)startNextOperation {
if ( !self.operations.count ) {
return;
}
if ( (int)self.runningOperations.count < self.maxConcurrentOperationCount ) {
NSOperation *nextOp = [self nextOperation];
if (nextOp) {
if ( !nextOp.isExecuting ) {
[self startOperation:nextOp];
}
else {
[self startNextOperation];
}
}
}
}
//
// Starts operations
//
- (void)startOperation:(NSOperation *)op {
void (^completion)() = [op.completionBlock copy];
NSOperation *blockOp = op;
[op setCompletionBlock:^{
if (completion) {
completion();
}
[self.runningOperations removeObject:blockOp];
[self.operations removeObject:blockOp];
[self startNextOperation];
}];
[self.runningOperations addObject:op];
[op start];
}
#pragma mark - Queue Information
//
// Returns next operation that is not already running
//
- (NSOperation *)nextOperation {
for (int i = 0; i < (int)self.operations.count; i++) {
NSOperation *operation = [self.operations objectAtIndex:i];
if ( ![self.runningOperations containsObject:operation] && !operation.isExecuting && operation.isReady ) {
return operation;
}
}
return nil;
}
@end