问题描述
||
Apple的Grand Central dispatch(GCD)很棒,但仅适用于iOS 4.0或更高版本。 Apple的文档说,“ [[A]序列化操作队列的行为与Grand Central dispatch中的串行调度队列不完全相同”(因为该队列不是FIFO,但是顺序由依赖项和优先级确定)。
在发布GCD之前支持OS版本的同时,实现与GCD的串行调度队列相同效果的正确方法是什么?或换一种说法,在想要支持低于4.0版本的iOS应用中,推荐的处理简单后台处理(进行Web服务请求等)的方法是什么?
解决方法
似乎人们将花很多精力来重写NSRunloop。根据NSRunloop文档:
您的应用程序无法创建
或显式管理NSRunLoop
对象。每个NSThread对象,
包括应用程序的主
线程,具有一个NSRunLoop对象
自动为其创建
需要。
因此,肯定的答案是创建一个可用的队列:
- (void)startRunLoop:(id)someObject
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[[NSRunLoop currentRunLoop] run];
[pool release];
}
...
NSThread *serialDispatchThread = [[NSThread alloc]
initWithTarget:self
selector:@selector(startRunLoop:)
object:nil];
[serialDispatchThread start];
要将任务添加到队列:
[object
performSelector:@selector(whatever:)
onThread:serialDispatchThread
withObject:someArgument
waitUntilDone:NO];
根据“运行循环”中的“线程编程指南”部分:
可可定义了自定义输入源
允许您执行选择器
在任何线程上。 ...执行选择器请求是
在目标线程上序列化,
减轻许多
可能会出现的同步问题
在运行多种方法时发生
在一个线程上。
因此,您已经有了一个明确的串行队列。当然,我的写得不是很出色,因为我已经告诉运行循环永远运行,您可能更喜欢稍后可以终止的运行方式,但这很容易修改。
, 这个PseudoSerialQueue怎么样?这是一个最小的实现,例如调度串行队列。
#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
如何使用:
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];
, 您可以使用NSOperationQueue
进行模拟,然后将任务计数设置为1。
编辑
-糟糕,应该仔细阅读。 fifo解决方案如下:
我想不出大多数ios开发人员会在您的情况下使用的方式。
我不害怕编写线程程序,因此这里是一种解决方案:
创建一个fifo工作者队列,该队列:
支持锁定
拥有一个NSOperationQueue
拥有一个NSOperation子类,该子类旨在在实施main
时将其从fifo队列中拉出。一次只能存在一个。
拥有要运行的NSArray的工作程序(定义一个工作程序取决于您-是NSInvocation,类,操作等)
NSOperation子类将工作程序从fifo工作程序队列中拉出,直到fifo工作程序队列用尽。
当fifo工作队列中有工作程序且没有活动的子操作时,它将创建一个子操作,并将其添加到其操作队列中。
如果您不习惯编写线程程序,则有一些陷阱-出于这个原因,该解决方案并不适合每个人,但是如果您已经熟悉使用所需的所有技术,则此解决方案的编写时间不会太长。
祝好运
, NSOperationQueue文档编写人员忘记了一些事情,实际上使这种实现显得微不足道。
将最大并发操作数设置为1保证仅是串行的
如果NSOperations是从同一线程添加到队列的。
我正在使用另一个选项,因为它可以正常工作。
从不同线程添加NSOperation,但使用NSCondition来管理队列。
可以使用performSelectorOnBackgroundThread调用startOperations(并且应该不要用锁来阻塞主线程)。
startOperations方法表示由一个或多个NSOperations组成的单个作业。
- (void)startOperations
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[[AppDelegate condition] lock];
while (![[[AppDelegate queue] operations] count] <= 0)
{
[[AppDelegate condition] wait];
}
NSOperation *newOperation = [alloc,init]....;
[[AppDelegate queue] addOperation:newOperation];
[[AppDelegate queue] waitUntilAllOperationsAreFinished]; // Don\'t forget this!
NSOperation *newOperation1 = [alloc,init]....;
[[AppDelegate queue] addOperation:newOperation1];
[[AppDelegate queue] waitUntilAllOperationsAreFinished]; // Don\'t forget this!
NSOperation *newOperation2 = [alloc,init]....;
[[AppDelegate queue] addOperation:newOperation2];
[[AppDelegate queue] waitUntilAllOperationsAreFinished]; // Don\'t forget this!
// Add whatever number operations you need for this single job
[[AppDelegate queue] signal];
[[AppDelegate queue] unlock];
[NotifyDelegate orWhatever]
[pool drain];
}
而已!
, 如果处理仍在后台进行,您是否真的需要严格按顺序进行处理?如果这样做,则只需设置依赖项就可以达到相同的效果,因此1依赖于0、2对1、3对2等,然后强制操作队列按顺序处理它们。将最大并发操作数设置为1,并且还保证队列是串行的。