http://www.cnblogs.com/cherri/archive/2010/12/03/1895541.html
一.多线程对于iPhone应用程序开发很重要
在一个程序中,这些独立运行的程序片断叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。多线程处理一个常见的例子就是用户界面设计,利用线程,用户可按下一个按钮,然后程序会立即作出响应,而不是让用户等待程序完成了当前任务以后才开始响应,这就是所谓的阻塞主线程使得界面被冻结。 特别是在一些基于网络的应用里,多线程技术显得尤为重要。当用户向服务器发出一个请求时,你需要做的有发送请求,接收数据,有时还需要解析数据(xml),但是如果你不是启动一个线程去做这些事情的话,那么做这些动作的时候你的界面将会被“冻结”,给用户造成不友好的体验。所以我们需要多线程技术。
二.NSOperationQueue
在这之前,你可能需要了解什么是NSOperation,这在官方文档里面写的很清楚。线程是从NSOperation中的main函数开始执行。类似于java中的多线程机制中的run函数。
下面让我们来看看NSOperationQueue的方法:
- (void)addOperation:(NSOperation *)operation
the specified operation remains in the queue until it finishes executing
创建一个NSOperationQueue对象,然后创建NSOperation添加到NSOperationQueue中。NSOperationQueue会为每一个NSOperation创建一个线程,并按照它们被添加的顺序执行(或者指定的顺序)。NSOperationQueue会自己处理autorelease pools 和其他的garbage,而不需要像在自己手动创建多线程时去费心管理这些事情,当把NSOperation的对象放进队列中,此线程就会按照先进先出的方式等待运行,并且是从main函数开始运行。
三.自定义协议与委托机制
协议就是约定好的一组方法,协议可以实现对象之间的通信,好像是一种通知或者回调机制:“在某件事情发生的时候通知我”,是cocoa设计模式的核心。经常与多线程编程搭配使用,用来通知主线程可以继续执行。
在下面的例子中我们看看这是怎么实现的:
首先,LazyTableAppDelegate.m里面
实现了NSURLConnection的代理,然后异步的等待数据下载结束之后,则从客户端启动另一个线程开始解析数据
1.ParseOperation *parser = [[ParseOperation alloc] initWithData:appListData delegate:self];
2.[queue addOperation:parser];
ParseOperation继承了NSOperation,然后将这个线程放入线程线程队列,应该是一个优先级队列,NSOperationQueue,如果此线程位于队列的头,则这个线程开始执行。线程的任务完成后,要通知主线程完成了相关的工作,那么什么时间返回主进程呢?这就用到了代理。[self.delegate didFinishParsing:self.workingArray] self.delegate代理指向的是是LazyTableAppDelegate的对象,这样就完成了对LazyTableAppDelegate的回调,需要注意的是,在LazyTableAppDelegate里实现了在ParseOperation.h里声明的那个协议,协议的声明方法见下面代码:
28 @protocol ParseOperationDelegate
29 - (void)didFinishParsing:(NSArray *)appList;
30 - (void)parseErrorOccurred:(NSError *)error;
31 @end
这里只是声明这些方法,在哪里定义呢,在需要被回调的地方定义,也就是LazyTableAppDelegate。
看一下回调的方法: [self.delegate didFinishParsing:self.workingArray];
这个方法是在线程ParseOperation执行结束的时候调用。self.delegate目前是指向的是LazyTableAppDelegate类的对象,这个对象则调用所继承的协议里面声明的方法- (void)didFinishParsing:(NSArray *)appList,这样就完成了回调。如何回到主线程的呢?看看(void)didFinishParsing:(NSArray *)appList这个函数是如何在LazyTableAppDelegate.m里定义的:
37 - (void)didFinishParsing:(NSArray *)appList
38 {
39 [self performSelectorOnMainThread:@selector(handleLoadedApps:) withObject:appList waitUntilDone:NO];
40 self.queue = nil;
41 }
一切真相大白,这个函数里面又调用了一个方法handleLoadedApps,并且使用performSelectorOnMainThread,使之在主线程中执行操作,这时候就可以释放我们的线程池了。一切又回归到主线程中了。
四.示例代码(官方程序:lazytable)
LazyTableAppDelegate.h
2 #import " AppRecord.h "
3 #import " ParseOperation.h "
4
5 @class RootViewController;
6
7 @interface LazyTableAppDelegate : NSObject < UIApplicationDelegate, ParseOperationDelegate >
8 {
9 UIWindow * window;
10 UINavigationController * navigationController;
11
12 // this view controller hosts our table of top paid apps
13 RootViewController * rootViewController;
14
15 // the list of apps shared with "RootViewController"
16 NSMutableArray * appRecords;
17
18 // the queue to run our "ParseOperation"
19 NSOperationQueue * queue;
20
21 // RSS feed network connection to the App Store
22 NSURLConnection * appListFeedConnection;
23 NSMutableData * appListData;
24 }
25
26 @property (nonatomic, retain) IBOutlet UIWindow * window;
27 @property (nonatomic, retain) IBOutlet UINavigationController * navigationController;
28 @property (nonatomic, retain) IBOutlet RootViewController * rootViewController;
29 @property (nonatomic, retain) NSMutableArray * appRecords;
30 @property (nonatomic, retain) NSOperationQueue * queue;
31 @property (nonatomic, retain) NSURLConnection * appListFeedConnection;
32 @property (nonatomic, retain) NSMutableData * appListData;
33
34 @end
35
LazyTableAppDelegate.m
2 #import " RootViewController.h "
3 #import " ParseOperation.h "
4
5 #import < CFNetwork / CFNetwork.h >
6
7 static NSString * const TopPaidAppsFeed =
8 @" http://phobos.apple.com/WebObjects/MZStoreServices.woa/ws/RSS/toppaidapplications/limit=75/xml " ;
9
10
11 @implementation LazyTableAppDelegate
12
13 @synthesize window, navigationController, appRecords, rootViewController, queue, appListFeedConnection, appListData;
14
15
16 #pragma mark -
17
18 - ( void )applicationDidFinishLaunching:(UIApplication * )application
19 {
20 // Configure and show the window
21 [window addSubview:[self.navigationController view]];
22 [window makeKeyAndVisible];
23 // self.appRecords,rootViewController.entries指向同一快内存
24 self.appRecords = [NSMutableArray array];
25 rootViewController.entries = self.appRecords;
26
27 NSURLRequest * urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:TopPaidAppsFeed]];
28 self.appListFeedConnection = [[[NSURLConnection alloc] initWithRequest:urlRequest delegate :self] autorelease];
29 }
30
31 - ( void )handleLoadedApps:(NSArray * )loadedApps
32 {
33 [self.appRecords addObjectsFromArray:loadedApps];
34 [rootViewController.tableView reloadData];
35 }
36
37 - ( void )didFinishParsing:(NSArray * )appList
38 {
39 [self performSelectorOnMainThread:@selector(handleLoadedApps:) withObject:appList waitUntilDone:NO];
40 self.queue = nil;
41 }
42
43 - ( void )parseErrorOccurred:(NSError * )error
44 {
45 [self performSelectorOnMainThread:@selector(handleError:) withObject:error waitUntilDone:NO];
46 }
47
48 #pragma mark -
49 #pragma mark NSURLConnection delegate methods
50
51 - ( void )handleError:(NSError * )error
52 {
53 ...
54 }
55
56 // The following are delegate methods for NSURLConnection. Similar to callback functions, this is how
57 // the connection object, which is working in the background, can asynchronously communicate back to
58 // its delegate on the thread from which it was started - in this case, the main thread.
59 //
60
61 // -------------------------------------------------------------------------------
62 // connection:didReceiveResponse:response
63 // -------------------------------------------------------------------------------
64 - ( void )connection:(NSURLConnection * )connection didReceiveResponse:(NSURLResponse * )response
65 {
66 self.appListData = [NSMutableData data]; // start off with new data
67 }
68
69 - ( void )connection:(NSURLConnection * )connection didReceiveData:(NSData * )data
70 {
71 [appListData appendData:data]; // append incoming data
72 }
73
74 - ( void )connection:(NSURLConnection * )connection didFailWithError:(NSError * )error
75 {
76 ...
77 }
78
79 - ( void )connectionDidFinishLoading:(NSURLConnection * )connection
80 {
81 self.appListFeedConnection = nil; // release our connection
82
83 [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
84
85 // create the queue to run our ParseOperation
86 self.queue = [[NSOperationQueue alloc] init];
87
88 // create an ParseOperation (NSOperation subclass) to the RSS feed data so that the UI is not blocked
89 // "ownership of appListData has been transferred to the parse operation and should no longer be
90 // referenced in this thread.
91 //
92 ParseOperation * parser = [[ParseOperation alloc] initWithData:appListData delegate :self];
93
94 [queue addOperation:parser]; // this will start the "ParseOperation"
95
96 [parser release];
97
98 // ownership of appListData has been transferred to the parse operation
99 // and should no longer be referenced in this thread
100 self.appListData = nil;
101 }
102
103 - ( void )dealloc
104 {
105 ...
106 [window release];
107 [super dealloc];
108 }
109
110 @end
ParseOperation.h
2 @protocol ParseOperationDelegate;
3
4 @interface ParseOperation : NSOperation
5 {
6 @private
7 id < ParseOperationDelegate > delegate ;
8
9 NSData * dataToParse;
10 NSMutableArray * workingArray;
11 AppRecord * workingEntry;
12 NSMutableString * workingPropertyString;
13 NSArray * elementsToParse;
14 BOOL storingCharacterData;
15 }
16 @property (nonatomic, assign) id < ParseOperationDelegate > delegate ;
17 @property (nonatomic, retain) NSData * dataToParse;
18 @property (nonatomic, retain) NSMutableArray * workingArray;
19 @property (nonatomic, retain) AppRecord * workingEntry;
20 @property (nonatomic, retain) NSMutableString * workingPropertyString;
21 @property (nonatomic, retain) NSArray * elementsToParse;
22 @property (nonatomic, assign) BOOL storingCharacterData;
23 - (id)initWithData:(NSData * )data delegate :(id < ParseOperationDelegate > )theDelegate;
24
25 @end
26
27 // 声明协议
28 @protocol ParseOperationDelegate
29 - ( void )didFinishParsing:(NSArray * )appList;
30 - ( void )parseErrorOccurred:(NSError * )error;
31 @end
32
ParseOperation.m
2 #import " AppRecord.h "
3 #import " LazyTableAppDelegate.h "
4
5 @implementation ParseOperation
6
7 @synthesize delegate , dataToParse, workingArray, workingEntry, workingPropertyString, elementsToParse, storingCharacterData;
8
9 - (id)initWithData:(NSData * )data delegate :(id < ParseOperationDelegate > )theDelegate
10 {
11 self = [super init];
12 if (self != nil)
13 {
14 self.dataToParse = data;
15 self. delegate = theDelegate;
16 self.elementsToParse = [NSArray arrayWithObjects:kIDStr, kNameStr, kImageStr, kArtistStr, nil];
17 }
18 return self;
19 }
20
21 - ( void )dealloc
22 {
23 [dataToParse release];
24 [workingEntry release];
25 [workingPropertyString release];
26 [workingArray release];
27
28 [super dealloc];
29 }
30
31
32 - ( void )main
33 {
34 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
35
36 self.workingArray = [NSMutableArray array];
37 self.workingPropertyString = [NSMutableString string ];
38
39 NSXMLParser * parser = [[NSXMLParser alloc] initWithData:dataToParse];
40 [parser setDelegate:self];
41 [parser parse];
42
43 if ( ! [self isCancelled])
44 {
45 [self. delegate didFinishParsing:self.workingArray];
46 }
47
48 self.workingArray = nil;
49 self.workingPropertyString = nil;
50 self.dataToParse = nil;
51
52 [parser release];
53 [pool release];
54 }
55
56
57 #pragma mark -
58 #pragma mark RSS processing
59
60 - ( void )parser:(NSXMLParser * )parser didStartElement:(NSString * )elementName
61 namespaceURI:(NSString * )namespaceURI
62 qualifiedName:(NSString * )qName
63 attributes:(NSDictionary * )attributeDict
64 {
65 ...
66 }
67 - ( void )parser:(NSXMLParser * )parser didEndElement:(NSString * )elementName
68 namespaceURI:(NSString * )namespaceURI
69 qualifiedName:(NSString * )qName
70 {
71 ...
72 }
73 - ( void )parser:(NSXMLParser * )parser foundCharacters:(NSString * ) string
74 {
75 ...
76 }
77 - ( void )parser:(NSXMLParser * )parser parseErrorOccurred:(NSError * )parseError
78 {
79 [ delegate parseErrorOccurred:parseError];
80 }
81 @end
82