Apple's Grand Central Dispatch (GCD) is great, but only works on iOS 4.0 or greater. Apple's documentation says, "[A] serialized operation queue does not offer quite the same behavior as a serial dispatch queue in Grand Central Dispatch does" (because the queue is not FIFO, but order is determined by dependencies and priorities).
What is the right way to achieve the same effect as GCD's serial dispatch queues while supporting OS versions before GCD was released? Or put another way, what is the recommended way to handle simple background processing (doing web service requests, etc.) in iOS apps that want to support versions less than 4.0?
Overview. Dispatch, also known as Grand Central Dispatch (GCD), contains language features, runtime libraries, and system enhancements that provide systemic, comprehensive improvements to the support for concurrent code execution on multicore hardware in macOS, iOS, watchOS, and tvOS.
Dispatch queues are FIFO queues to which your application can submit tasks in the form of block objects. Dispatch queues execute tasks either serially or concurrently. Work submitted to dispatch queues executes on a pool of threads managed by the system.
Serial queues (also known as private dispatch queues) execute one task at a time in the order in which they are added to the queue. The currently executing task runs on a distinct thread (which can vary from task to task) that is managed by the dispatch queue.
Grand Central Dispatch (GCD) is a low-level API for managing concurrent operations. It can help improve your app's responsiveness by deferring computationally expensive tasks to the background. It's an easier concurrency model to work with than locks and threads.
How about this PseudoSerialQueue? It is a minimal implementation like the Dispatch Serial Queue.
#import <Foundation/Foundation.h>
@interface PseudoTask : NSObject
{
id target_;
SEL selector_;
id queue_;
}
@property (nonatomic, readonly) id target;
- (id)initWithTarget:(id)target selector:(SEL)selector queue:(id)queue;
- (void)exec;
@end
@implementation PseudoTask
@synthesize target=target_;
- (id)initWithTarget:(id)target selector:(SEL)selector queue:(id)queue;
{
self = [super init];
if (self) {
target_ = [target retain];
selector_ = selector;
queue_ = [queue retain];
}
return self;
}
- (void)exec
{
[target_ performSelector:selector_];
}
- (void)dealloc
{
[target_ release];
[queue_ release];
}
@end
@interface PseudoSerialQueue : NSObject
{
NSCondition *condition_;
NSMutableArray *array_;
NSThread *thread_;
}
- (void)addTask:(id)target selector:(SEL)selector;
@end
@implementation PseudoSerialQueue
- (id)init
{
self = [super init];
if (self) {
array_ = [[NSMutableArray alloc] init];
condition_ = [[NSCondition alloc] init];
thread_ = [[NSThread alloc]
initWithTarget:self selector:@selector(execQueue) object:nil];
[thread_ start];
}
return self;
}
- (void)addTask:(id)target selector:(SEL)selector
{
[condition_ lock];
PseudoTask *task = [[PseudoTask alloc]
initWithTarget:target selector:selector queue:self];
[array_ addObject:task];
[condition_ signal];
[condition_ unlock];
}
- (void)quit
{
[self addTask:nil selector:nil];
}
- (void)execQueue
{
for (;;) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[condition_ lock];
while (array_.count == 0)
[condition_ wait];
PseudoTask *task = [array_ objectAtIndex:0];
[array_ removeObjectAtIndex:0];
[condition_ unlock];
if (!task.target) {
[pool drain];
break;
}
[task exec];
[task release];
[pool drain];
}
}
- (void)dealloc
{
[array_ release];
[condition_ release];
}
@end
How to use:
PseudoSerialQueue *q = [[[PseudoSerialQueue alloc] init] autorelease];
[q addTask:self selector:@selector(test0)];
[q addTask:self selector:@selector(test1)];
[q addTask:self selector:@selector(test2)];
[q quit];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With