如何使用NSOperations和NSOperationQueues(二)

来源:互联网 发布:js 只能输入两位小数 编辑:程序博客网 时间:2024/05/25 19:57

NSOperation API


NSOperation 类有一个相当简短的声明。要定制一个操作,可以遵循以下步骤:

1.继承NSOperation类

2.重写“main”方法

3.在“main”方法中创建一个“autoreleasepool”

4.将你的代码放在“autoreleasepool”中

创建你自己的自动释放池的原因是,你不能访问主线程的自动释放池,所以你应该自己创建一个。以下是一个例子:
#import 
 
@interface MyLengthyOperation: NSOperation
@end
@implementation MyLengthyOperation
 
- (void)main {
    // a lengthy operation
    @autoreleasepool {
        for (int i = 0 ; i < 10000 ; i++) {
        NSLog(@"%f", sqrt(i));
        }
    }
}
 
@end

上面的例子代码展示了ARC语法在自动释放池中的使用。你现在必须使用ARC了!:]

在线程操作中,你从来都不能明确知道,一个操作什么时候会开始,要持续多久才能结束。在大多数时候,如果用户滑动离开了页面,你并不想在后台执行一个操作 – 没有任何的理由让你去执行。这里关键是要经常地检查NSOperation类的isCancelled属性。例如,在上面的例子程序中,你会这样做:

@interface MyLengthyOperation: NSOperation
@end
 
@implementation MyLengthyOperation
- (void)main {
    // a lengthy operation
    @autoreleasepool {
        for (int i = 0 ; i < 10000 ; i++) {
 
        // is this operation cancelled?
        if (self.isCancelled)
            break;
 
        NSLog(@"%f", sqrt(i));
        }
    }
}
@end

为了取消这个操作,你应该调用NSOperation的取消方法,正如下面的:

// In your controller class, you create the NSOperation
// Create the operation
MyLengthyOperation *my_lengthy_operation = [[MyLengthyOperation alloc] init];
.
.
.
// Cancel it
[my_lengthy_operation cancel];

=======================================================================================


NSOperation类有一些其他的方法和属性:

开始(start):通常,你不会重写这个方法。重写“start”方法需要相对复杂的实现,你还需要注意像isExecuting,isFinished,isConcurrent和isReady这些属性。当你将一个操作添加到一个队列当中时(一个NSOperationQueue的实例,接下来会讨论的),这个队列会在操作中调用“start”方法然后它会做一些准备和“main”方法的后续操作。假如你在一个NSOperation实例中调用了“start”方法,如果没有把它添加到一个队列中,这个操作会在main loop中执行。

从属性(Dependency):你可以让一个操作从属于其他的操作。任何操作都可以从属于任意数量的操作。当你让操作A从属于操作B时,即使你调用了操作A的“start”方法,它会等待操作B结束后才开始执行。例如:

MyDownloadOperation *downloadOp = [[MyDownloadOperation alloc] init]; // MyDownloadOperation is a subclass of NSOperation
MyFilterOperation *filterOp = [[MyFilterOperation alloc] init]; // MyFilterOperation  is a subclass of NSOperation
         
[filterOp addDependency:downloadOp];

要删除依赖性:
[filterOp removeDependency:downloadOp];

优先级(Priority):有时候你希望在后台运行的操作并不是很重要的,它可以以较低的优先级执行。可以通过使用“setQueuePriority:”方法设置一个操作的优先级。
 
[filterOp setQueuePriority:NSOperationQueuePriorityVeryLow]; 

其他关于设置线程优先级的选择有: NSOperationQueuePriorityLow, NSOperationQueuePriorityNormal, NSOperationQueuePriorityHigh和NSOperationQueuePriorityVeryHigh.

当你添加了操作到一个队列时,在对操作调用“start”方法之前,NSOperationQueue会浏览所有的操作。那些有较高优先级的操作会被先执行。有同等优先级的操作会按照添加到队列中的顺序去执行(先进先出)。

(历史注释:在1997年,火星车中的嵌入式系统遭遇过优先级反转问题,也许这是说明正确处理优先级和互斥锁的最昂贵示例了。

Completion block:在NSOperation 类中另一个有用的方法叫setCompletionBlock:。一旦操作完成了,如果你还有一些事情想做,你可以把它放在一个块中,并且传递给这个方法。这个块会在主线程中执行。 

其他一些关于处理线程的提示:

如果你需要传递一些值和指针到一个线程中,创建你自己的指定初始化方法是一个很好的尝试: 

#import Foundation/Foundation.h
 
@interface MyOperation : NSOperation
 
-(id)initWithNumber:(NSNumber *)start string:(NSString *)string;
 
@end

如果你的操作需要有一个返回值或者对象,声明一个委托方法是不错的选择。记住委托方法必须在主线程中返回。然而,因为你要继承NSOperation类,你必须先将这个操作类强制转换为NSObject对象。可以按照以下步骤去做: 
#import

@interfaceMyOperation:NSOperation

-(id)initWithNumber:(NSNumber*)startstring:(NSString*)string;

@end

要经常检查isCancelled属性。如果操作不需要被执行了,你就不想在后台去运行它了!

你并不需要重写“start”方法。然而,如果你决定去重写“start”方法,就必须处理好像isExecuting,isFinished,isConcurrent和isReady这些属性。否则你的操作类不会正确的运作。

你一旦添加了一个操作到一个队列(NSOperationQueue的一个实例)中,就要负责释放它(如果你不使用ARC的话)。NSOperationQueue获得操作对象的所有权,调用“start”方法,然后结束时负责释放它。

你不能重用一个操作对象。一旦它被添加到一个队列中,你就丧失了对它的所有权。如果你想再使用同一个操作类,就必须创建一个新的实例变量。

一个结束的操作不能被重启。

如果你取消了一个操作,它不会马上就发生。它会在未来的某个时候某人在“main”函数中明确地检查isCancelled==YES时被取消掉;否则,操作会一直执行到完成为止。

一个操作是否成功地完成,失败了,或者是被取消了,isFinished的值总会被设置为YES。所以千万不要觉得isFinished==YES就表示所有的事情都顺利完成了—特别的,如果你在代码里面有从属性(dependencies),就要更加注意!



NSOperationQueue API



NSOperationQueue 也有一个相当简单的界面。它甚至比NSOperation还要简单,因为你不需要去继承它,或者重写任何的方法 — 你可以简单创建一个。给你的队列起一个名字会是一个不错的做法;这样你可以在运行时识别出你的操作队列,并且让调试变得更简单:

NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
myQueue.name = @"Download Queue";

并发操作:队列和线程是两个不同的概念。一个队列可以有多个线程。每个队列中的操作会在所属的线程中运行。举个例子你创建一个队列,然后添加三个操作到里面。队列会发起三个单独的线程,然后让所有操作在各自的线程中并发运行。

到底有多少个线程会被创建?这是个很好的问题!这取决与硬件。默认情况下,NSOperationQueue类会在场景背后施展一些魔法,决定如何在特定的平台下运行代码是最好的,并且会尽量启用最大的线程数量。考虑以下的例子。假设系统是空闲的,并且有很多的可用资源,这样NSOperationQueue会启用比如8个同步线程。下次你运行程序,系统会忙于处理其他不相关的操作,它们消耗着资源,然后NSOperationQueue只会启用两个同步线程了。

并发操作的最大值:你可以设定NSOperationQueue可以并发运行的最大操作数。NSOperationQueue会选择去运行任何数量的并发操作,但是不会超过最大值。

myQueue.MaxConcurrentOperationCount = 3;

如果你改变了主意,想将MaxConcurrentOperationCount设置回默认值,你可以执行下列操作:

myQueue.MaxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount; 

添加操作:一个操作一旦被添加到一个队列中,你就应该通过传送一个release消息给操作对象(如果使用了手动引用计数,非ARC的话),然后队列会负责开始这个操作。从这点上看,什么时候调用“start”方法由这个队列说了算。 
[myQueue addOperation:downloadOp]; 
[downloadOp release]; // manual reference counting 

待处理的操作:任何时候你可以询问一个队列哪个操作在里面,并且总共有多少个操作在里面。记住只有那些等待被执行的操作,还有那些正在运行的操作,会被保留在队列中。操作一完成,就会退出队列。 
NSArray *active_and_pending_operations = myQueue.operations;
NSInteger count_of_operations = myQueue.operationCount; 

暂停队列:你可以通过设定setSuspended:YES来暂停一个队列。这样会暂停所有在队列中的操作 — 你不能单独的暂停操作。要重新开始队列,只要简单的setSuspended:NO。 
// Suspend a queue
[myQueue setSuspended:YES];
.
.
.
// Resume a queue
[myQueue setSuspended: NO]; 

取消操作:要取消一个队列中的所有操作,你只要简单的调用“cancelAllOperations”方法即可。还记得之前提醒过经常检查NSOperation中的isCancelled属性吗?

原因是“cancelAllOperations”并没有做太多的工作,他只是对队列中的每一个操作调用“cancel”方法 — 这并没有起很大作用!:] 如果一个操作并没有开始,然后你对它调用“cancel”方法,操作会被取消,并从队列中移除。然而,如果一个操作已经在执行了,这就要由单独的操作去识别撤销(通过检查isCancelled属性)然后停止它所做的工作。

[myQueuecancelAllOperations];

addOperationWithBlock: 如果你有一个简单的操作不需要被继承,你可以将它当做一个块(block)传递给队列。如果你需要从块那里传递回任何数据,记得你不应该传递任何强引用的指针给块;相反,你必须使用弱引用。而且,如果你想要在块中做一些跟UI有关的事情,你必须在主线程中做。

UIImage *myImage = nil;
 
// Create a weak reference
__weak UIImage *myImage_weak = myImage;
 
// Add an operation as a block to a queue
[myQueue addOperationWithBlock: ^ {
 
    // a block of operation
    NSURL *aURL = [NSURL URLWithString:@"http://www.somewhere.com/image.png"];
    NSError *error = nil;
    NSData *data = [NSData dataWithContentsOfURL:aURL options:nil error:&error];
    If (!error)
        [myImage_weak imageWithData:data];
 
    // Get hold of main queue (main thread)
    [[NSOperationQueue mainQueue] addOperationWithBlock: ^ {
        myImageView.image = myImage_weak; // updating UI
    }];
 

}]; 

摘自:http://www.cocoachina.com/industry/20121010/4900.html

0 0
原创粉丝点击