慶幸的是,Apple在OS X 10.5 Leopard上做了很多改進。NSThread本身就新增了很多新的方法,從而使得多線程變得更加容易。此外還新增了NSOperation和NSOperationQueue兩個類。該教程通過一個簡單的實例來介紹如何使用這些新增類並如何讓多線程的應用變得小菜一碟。
你可以從此獲取該項目的實例代碼: Async
Downloader Example Project
在該教程中,我會介紹一種使用NSOperation和NSOperationQueue在後臺線程中很好地處理任務的方法。該教程的目標是介紹這些類的使用,但並不排除使用它們的其他方法。
如果你熟悉Java或一個它的變種語言,NSOperation就和java.lang.Runnable接口很相似。和Java的Runnable一樣,NSOperation也是設計用來擴展的,並且最低僅需重寫一個方法。對於NSOperation這個方法是-(void)main。一個使用NSOperation的最簡單方法就是將其放入NSOperationQueue中。一旦一個操作被加入隊列,該隊列就會啓動並開始處理它。一旦該操作完成隊列就會釋放它。
NSOperation實例
在該實例中,我編寫了一段獲取網頁內容的字符串,然後解析成NSXMLDocument對象並在結束前傳回應用主線程。
PageLoadOperation.h
- #import <Cocoa/Cocoa.h>
- @interface PageLoadOperation : NSOperation {
- NSURL *targetURL;
- }
- @property(retain) NSURL *targetURL;
- - (id)initWithURL:(NSURL*)url;
- @end
- #import "PageLoadOperation.h"
- #import "AppDelegate.h"
- @implementation PageLoadOperation
- @synthesize targetURL;
- - (id)initWithURL:(NSURL*)url;
- {
- if (![super init]) return nil;
- [self setTargetURL:url];
- return self;
- }
- - (void)dealloc {
- [targetURL release], targetURL = nil;
- [super dealloc];
- }
- - (void)main {
- NSString *webpageString = [[[NSString alloc] initWithContentsOfURL:[self targetURL]] autorelease];
- NSError *error = nil;
- NSXMLDocument *document = [[NSXMLDocument alloc] initWithXMLString:webpageString
- options:NSXMLDocumentTidyHTML
- error:&error];
- if (!document) {
- NSLog(@"%s Error loading document (%@): %@", _cmd, [[self targetURL] absoluteString], error);
- return;
- }
- [[AppDelegate shared] performSelectorOnMainThread:@selector(pageLoaded:)
- withObject:document
- waitUntilDone:YES];
- [document release];
- }
- @end
我們可以看到,這個類很簡單。它在init方法中接受一個URL並保存起來。在調用main方法的時候它就從URL中構建一個字符串並傳給NSXMLDocument的init方法。如果在加載xml文檔過程中沒有發生錯誤,該文檔就會被傳回到主線程的AppDelegate,然後任務結束。隊列會在NSOperation的main方法結束後自動釋放該對象。
AppDelegate.h
- #import <Cocoa/Cocoa.h>
- @interface AppDelegate : NSObject {
- NSOperationQueue *queue;
- }
- + (id)shared;
- - (void)pageLoaded:(NSXMLDocument*)document;
- @end
AppDelegate.m
- #import "AppDelegate.h"
- #import "PageLoadOperation.h"
- @implementation AppDelegate
- static AppDelegate *shared;
- static NSArray *urlArray;
- - (id)init
- {
- if (shared) {
- [self autorelease];
- return shared;
- }
- if (![super init]) return nil;
- NSMutableArray *array = [[NSMutableArray alloc] init];
- [array addObject:@"http://www.google.com"];
- [array addObject:@"http://www.apple.com"];
- [array addObject:@"http://www.yahoo.com"];
- [array addObject:@"http://www.zarrastudios.com"];
- [array addObject:@"http://www.macosxhints.com"];
- urlArray = array;
- queue = [[NSOperationQueue alloc] init];
- shared = self;
- return self;
- }
- - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
- {
- for (NSString *urlString in urlArray) {
- NSURL *url = [NSURL URLWithString:urlString];
- PageLoadOperation *plo = [[PageLoadOperation alloc] initWithURL:url];
- [queue addOperation:plo];
- [plo release];
- }
- }
- - (void)dealloc
- {
- [queue release], queue = nil;
- [super dealloc];
- }
- + (id)shared;
- {
- if (!shared) {
- [[AppDelegate alloc] init];
- }
- return shared;
- }
- - (void)pageLoaded:(NSXMLDocument*)document;
- {
- NSLog(@"%s Do something with the XMLDocument: %@", _cmd, document);
- }
- @end
在實例中AppDelegate做了兩件事。其一是在init方法中初始化了NSOperationQueue並載入了一個urls數組。在應用結束加載時NSApplication 實例會調用applicationDidFinishLaunching:方法,AppDelegate就遍歷每個url並創建相應的任務,然後將任務加入隊列中。在隊列中每加入一個人後,隊列都會爲其分配一個NSThread來啓動它,線程就會運行操作的main方法。一旦操作完成,線程就會報告給隊列以讓隊列釋放該操作。
NSOperationQueue的併發
在這個簡單的實例中,由於很難再隊列中加入足夠多的任務使得我們很難看到它們是併發運行的。但如果你的任務需要更多的時間,你就可以看到隊列是同時運行所有的任務。如果你想限制並行運行的任務數目,你可以在AppDelegate的init方法中做如下修改。
- - (id)init
- {
- if (shared) {
- [self autorelease];
- return shared;
- }
- if (![super init]) return nil;
- NSMutableArray *array = [[NSMutableArray alloc] init];
- [array addObject:@"http://www.google.com"];
- [array addObject:@"http://www.apple.com"];
- [array addObject:@"http://www.yahoo.com"];
- [array addObject:@"http://www.zarrastudios.com"];
- [array addObject:@"http://www.macosxhints.com"];
- urlArray = array;
- queue = [[NSOperationQueue alloc] init];
- [queue setMaxConcurrentOperationCount:2];
- shared = self;
- return self;
- }
在這個修改的init方法中,隊列被限制只能同時運行兩個操作。剩餘的操作就需要等待之前的兩個操作有一個完成後纔有機會被運行。
結論
這是NSOperation和NSOperationQueue最基本的使用。你可以注意到該實例的大部分代碼和NSOperation和NSOperationQueue 的設定和使用無關。NSOperation所需要的代碼是驚人地少。但是通過這少量的代碼你就可以在你的應用中輕鬆地使用多線程以爲用戶提供更好的用戶體驗並可以更好地優化複雜任務。
原文鏈接:http://www.cimgf.com/2008/02/16/cocoa-tutorial-nsoperation-and-nsoperationqueue/