NSOperation

Apple在OS X 10.5 Leopard上做了很多改进。NSThread本身就新增了很多新的方法,从而使得多线程变得更加容易。此外还新增了NSOperationNSOperationQueue两个类,使多线程编程更加方便!

NSOperationNSOperationQueue为例

1.头文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11

//  QTileDownloadOperation.h
#import <Foundation/Foundation.h>

@interface QTileDownloadOperation : NSOperation

@property(nonatomic, retain)NSString* name;

- (void)clear;

@end

2.实现文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//  QTileDownloadOperation.m
#import "QTileDownloadOperation.h"

@implementation QTileDownloadOperation
@synthesize name;

- (void)dealloc {
    [name release];

    //......
    [super dealloc];
}


- (void)main
{
    if ([self isCancelled] || [self isFinished]) {
        return;
    }

    NSLog(@"%@start!",name);
    NSString* urlString = [@"你得URL" stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

    NSURL* url = [NSURL URLWithString:urlString];
    NSMutableURLRequest* requestWithAgent = [NSMutableURLRequest requestWithURL:url];
    
    [requestWithAgent setValue:@"mapapi" forHTTPHeaderField:@"User-Agent"];
    [requestWithAgent setTimeoutInterval:120];
    
    NSError* error = nil;
    
    NSData *data = [NSURLConnection sendSynchronousRequest:requestWithAgent returningResponse:nil error:&error];
    
    if (error == nil) {
        
        if (![self isCancelled])
        {
            [self didFinishWithData:data];
        }
    }
    else
    {
       //error handle
       //对外通知,注意线程同步
    }
}

- (void)didFinishWithData:(NSData*)data
{
    NSLog(@"%@complete!",name);
    //数据处理.........................
    //对外通知,注意线程同步
}

- (void)clear
{
    //可以进行取消网络,delegate置为nil,清理其他资源等。主要是防止线程对象释放时Crash
}

@end

3.可以使用创建的OperationQueue管理线程对象

1
2
3
4
5
    NSOperationQueue* q = [[NSOperationQueue alloc] init];
    self.myQueue = q;
    //设置允许最大并发数
    [myQueue setMaxConcurrentOperationCount:2];
    [q release];

NSOperationQueue中添加QTileDownloadOperation对象

1
2
3
4
5
6
7
8
    for (int i = 0; i < 8; ++i) {
        
        QTileDownloadOperation* op = [[QTileDownloadOperation alloc] init];
        NSString* name = [NSString stringWithFormat:@"op%d",i];
        op.name = name;
        [myQueue addOperation:op];
        [op release];
    }

释放线程队列

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
    NSArray *allOperations = [myQueue operations];
    
    for (QTileDownloadOperation* op in allOperations) {
        
        if(![op isCancelled])
        {
            [op clear];
            [op cancel];
        }
    }
    
    [myQueue release];
    myQueue = nil;

4.如何是取消NSOperation

通常情况下,取消当前线程队列中当前待执行线程队列队首尚未取消的线程对象。如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    NSArray *allOperations = [myQueue operations];
    for (int i = [myQueue maxConcurrentOperationCount];  i < [allOperations count]; ++i) {
        QTileDownloadOperation* op = [allOperations objectAtIndex:i];
        
        if (!op.isCancelled) {
            
            [op clear];
            [op cancel];
        }
    }

为什么会allOperations暂存呢?

因为线程队列中的对象在并发执行,其状态在任意时间点可能会改变。

而这个取消的操作通常在其他线程中,比如说主线程,会因线程不同步引发Crash等异常。