NSOperation
1.继承NSOperation类
2.重写“main”方法
3.在“main”方法中创建一个“autoreleasepool”
4.将你的代码放在“autoreleasepool”中
取消任务:
这里关键是要经常地检查NSOperation类的isCancelled属性。
- (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));
}
}
}
为了取消这个操作,你应该调用NSOperation的取消方法,正如下面的:
[my_lengthy_operation cancel];
从属性(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会浏览所有的操作。那些有较高优先级的操作会被先执行。有同等优先级的操作会按照添加到队列中的顺序去执行(先进先出)。
Completion block:
在NSOperation 类中另一个有用的方法叫setCompletionBlock:。一旦操作完成了,如果你还有一些事情想做,你可以把它放在一个块中,并且传递给这个方法。这个块会在主线程中执行。
你一旦添加了一个操作到一个队列(NSOperationQueue的一个实例)中,就要负责释放它(如果你不使用ARC的话)。NSOperationQueue获得操作对象的所有权,调用“start”方法,然后结束时负责释放它。
你不能重用一个操作对象。一旦它被添加到一个队列中,你就丧失了对它的所有权。如果你想再使用同一个操作类,就必须创建一个新的实例变量。
一个结束的操作不能被重启。
一个操作是否成功地完成,失败了,或者是被取消了,isFinished的值总会被设置为YES。所以千万不要觉得isFinished==YES就表示所有的事情都顺利完成了—特别的,如果你在代码里面有从属性(dependencies),就要更加注意!
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];
取消操作:
要取消一个队列中的所有操作,你只要简单的调用“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
}];
}];