代码改变世界

IOS学习之路七(使用 Operation 异步运行任务)

2013-08-09 17:17  Lves Li  阅读(179)  评论(0编辑  收藏  举报

application delegate 头文件(.h)中声明一个 operation 队列和两个 invocation operations

#import <UIKit/UIKit.h>
@interface Running_Tasks_Asynchronously_with_OperationsAppDelegate
: UIResponder <UIApplicationDelegate> @property (nonatomic, strong) UIWindow *window;
@property (nonatomic, strong) NSOperationQueue *operationQueue; @property (nonatomic, strong) NSInvocationOperation *firstOperation; @property (nonatomic, strong) NSInvocationOperation *secondOperation; @end

application delegate 的实现文件(.m 文件)如下: 

#import "Running_Tasks_Asynchronously_with_OperationsAppDelegate.h" @implementation Running_Tasks_Asynchronously_with_OperationsAppDelegate @synthesize window = _window;
@synthesize firstOperation;
@synthesize secondOperation;
@synthesize operationQueue;
- (void) firstOperationEntry:(id)paramObject{
NSLog(@"%s", __FUNCTION__);
NSLog(@"Parameter Object = %@", paramObject); NSLog(@"Main Thread = %@", [NSThread mainThread]); NSLog(@"Current Thread = %@", [NSThread currentThread]);
}
- (void) secondOperationEntry:(id)paramObject{
NSLog(@"%s", __FUNCTION__);
NSLog(@"Parameter Object = %@", paramObject); NSLog(@"Main Thread = %@", [NSThread mainThread]); NSLog(@"Current Thread = %@", [NSThread currentThread]);
}
- (BOOL) application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
NSNumber *firstNumber = [NSNumber numberWithInteger:111]; NSNumber *secondNumber = [NSNumber numberWithInteger:222];
self.firstOperation =[[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(firstOperationEntry:)
object:firstNumber]; self.secondOperation = [[NSInvocationOperation alloc]
initWithTarget:self selector:@selector(secondOperationEntry:) object:secondNumber];
self.operationQueue = [[NSOperationQueue alloc] init]; /* Add the operations to the queue */

[self.operationQueue addOperation:self.firstOperation]; [self.operationQueue addOperation:self.secondOperation];
NSLog(@"Main thread is here");
self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible];
return YES;
} @end

在实现的代码里面都发生了什么呢:
有两个方法:firstOperationEntry:secondOperationEntry:,每个方法都接收一个对象 

作为参数,并且在控制台窗口打印出当前线程、主线程和参数。这两个入口函数的

invocation operation 将被添加到一个 operation 队列中。
我们初始化两个 NSInvocationOperation 类型对象,并给每个 operation 设置目标

selector 入口点,如之前所述。
然后我们初始化一个 NSOperationQueue 类型对象。(当然也可以在入口方法前面创

建)队列对象将负责管理 operation 对象的并发。
我们调用 NSOperationQueue 的实例方法 addOperation:把每个 invocation operation 添加

operation 队列中。在这里,operation 队列可能会也可能不会立即通过 nvocationoperation start 方法启动 invocation operation。但是,需要牢记重要的一点:添加operations operation 队列后,你不能手动启动 operations,必须交由 operation 队列负责。

现在,我们运行一次示例代码,在控制台窗口可以看到如下结果:

[Running_Tasks_Asynchronously_with_OperationsAppDelegate firstOperationEntry:]Main thread is here
Parameter Object = 111
[Running_Tasks_Asynchronously_with_OperationsAppDelegate secondOperationEntry:]Main Thread = <NSThread: 0x6810260>{name = (null), num = 1}

Parameter Object = 222
Current Thread = <NSThread: 0x6805c20>{name = (null), num = 3}Main Thread = <NSThread: 0x6810260>{name = (null), num = 1}Current Thread = <NSThread: 0x6b2d1d0>{name = (null), num = 4} 


如果我们子类化了一个 NSOperation 类,并且把这个子类的实例对象添加到了 operation队列,我们需要做稍微的改动。记住以下几点:

由于当把 NSOperation 的子类对象添加到一个 operation 队列中,该对象会异步运行。由此,你必须 overrideNSOperation 的实例方法 isConcurrent,在该方法中返回 YESstart 方法里面执行 main 任务之前,需要定期的调用 isCancelled 方法来检测该函数的返回值,以确定是退出 start 方法还是开始运行 operation。在这里,当 operation 添加到队列中后,operation start 方法将会被 operation 队列调用,start 方法中,调用isCanelled 方法确定 operation 是否被取消。如果 operation 被取消了,只需要从 start 

方法中简单的返回即可。如果没被取消,会在 start 方法中调用 main 方法。

main task 实现部分中 override main 函数,main 函数将被 operation 执行。在这个函数里面确保分配和初始化 autorelease pool,并且在返回之前释放这个 pool

重载 operation isFinished isExecuting 方法,这两个函数返回对应的 BOOL 值,代表 operation 是执行完毕还是在执行中。

下面是我们的 operation 声明(.h 文件)

#import <Foundation/Foundation.h>
@interface SimpleOperation : NSOperation
/* Designated Initializer */
- (id) initWithObject:(NSObject *)paramObject; @end
The implementation of the operation is as follows:
#import "SimpleOperation.h" @implementation SimpleOperation NSObject *givenObject;
BOOL finished;
BOOL executing;
- (id) init {
NSNumber *dummyObject = [NSNumber numberWithInteger:123]; return([self initWithObject:dummyObject]);
}
- (id) initWithObject:(NSObject *)paramObject{
self = [super init];
if (self != nil){
/* Keep these values for the main method */
givenObject = paramObject;
}
return(self);
}
- (void) start {
/* If we are cancelled before starting, then
we have to return immediately and generate the
required KVO notifications */
if ([self isCancelled]){
/* If this operation *is* cancelled */
/* KVO compliance */
[self willChangeValueForKey:@"isFinished"];
finished = YES;
[self didChangeValueForKey:@"isFinished"];
return;

} else {
/* If this operation is *not* cancelled */
/* KVO compliance */
[self willChangeValueForKey:@"isExecuting"]; executing = YES;
/* Call the main method from inside the start method */ [self main];
[self didChangeValueForKey:@"isExecuting"];
}
}
- (void) main {
@try {
@autoreleasepool {
/* Keep a local variable here that must get set to YES whenever we are done with the task */
BOOL taskIsFinished = NO;
/* Create a while loop here that only exists
if the taskIsFinished variable is set to YES or
the operation has been cancelled */
while (taskIsFinished == NO &&
[self isCancelled] == NO){
/* Perform the task here */
NSLog(@"%s", __FUNCTION__);
NSLog(@"Parameter Object = %@", givenObject); NSLog(@"Main Thread = %@", [NSThread mainThread]); NSLog(@"Current Thread = %@", [NSThread currentThread]); /* Very important. This way we can get out of the
loop and we are still complying with the cancellation
rules of operations */
taskIsFinished = YES;
}
/* KVO compliance. Generate the
required KVO notifications */
[self willChangeValueForKey:@"isFinished"]; [self willChangeValueForKey:@"isExecuting"]; finished = YES;
executing = NO;
[self didChangeValueForKey:@"isFinished"]; [self didChangeValueForKey:@"isExecuting"]; }
}
@catch (NSException * e) { NSLog(@"Exception %@", e);
}
}
- (BOOL) isConcurrent{
return YES;
}
- (BOOL) isFinished{
/* Simply return the value */
return finished;
}
- (BOOL) isExecuting{
/* Simply return the value */
return executing;
}
@end

现在可以在其他任何类中使用上面定义的这个 operation 类了,比如在 applicationdelegate 中。下面是 application delegate 的声明,使用了新的 operation 类,并将其添加到了新的 operation 队列中: 

#import <UIKit/UIKit.h>
@class SimpleOperation;
@interface Running_Tasks_Asynchronously_with_OperationsAppDelegate : UIResponder <UIApplicationDelegate>
@property (nonatomic, strong) UIWindow *window;
@property (nonatomic, strong) NSOperationQueue *operationQueue; @property (nonatomic, strong) SimpleOperation *firstOperation; @property (nonatomic, strong) SimpleOperation *secondOperation;
@end

application delegate 的实现部分如下: 

#import "Running_Tasks_Asynchronously_with_OperationsAppDelegate.h" #import "SimpleOperation.h"
@implementation Running_Tasks_Asynchronously_with_OperationsAppDelegate @synthesize window = _window;
@synthesize firstOperation;
@synthesize secondOperation;
@synthesize operationQueue;
- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ NSNumber *firstNumber = [NSNumber numberWithInteger:111]; NSNumber *secondNumber = [NSNumber numberWithInteger:222]; self.firstOperation = [[SimpleOperation alloc]
initWithObject:firstNumber];
self.secondOperation = [[SimpleOperation alloc] initWithObject:secondNumber];
self.operationQueue = [[NSOperationQueue alloc] init]; /* Add the operations to the queue */ [self.operationQueue addOperation:self.firstOperation]; [self.operationQueue addOperation:self.secondOperation]; NSLog(@"Main thread is here");
self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible];
return YES;
}
@end

打印到控制台窗口的结果与之前使用并发 invocation operation 类似: 

Main thread is here
-[SimpleOperation main]
-[SimpleOperation main]
Parameter Object = 222
Parameter Object = 222
Main Thread = <NSThread: 0x6810260>{name = (null), num = 1}Main Thread = <NSThread: 0x6810260>{name = (null), num = 1}Current Thread = <NSThread: 0x6a10b90>{name = (null), num = 3}Current Thread = <NSThread: 0x6a13f50>{name = (null), num = 4}