最近在網上閑逛,看到一個老外的博客,感覺里面iphone開表的文章相當不錯,自己就暫且選了一篇進行翻譯,以備日后使用。這里有這個博客的鏈接,有興趣的可以看下(http://www.cimgf.com/)。下面是Cocoa Tutorial: NSOperation and NSOperationQueue這篇博文的原文翻譯。
在任何語言中線程都是困難的。更糟糕的是,當線程出錯的時候,會出現意想不到的情況。因為這些,程序員要么完全的避免線程,要么就花費大量的時間確保線程萬無一失。
值得慶幸的是,蘋果在獅子系統中對此做了大量的改進。利用NSThread封裝了大量的方法,以便於線程容易管理。另外,蘋果介紹了兩個新的對象:NSOperation和NSOperationQueue。這個教程,我將利用一些簡單的例子,展示如何使用這些新對象和怎么使多線程應用程序順利執行。
下面的網址你可以獲得工程的例子:http://www.cimgf.com/wp-content/uploads/2008/02/asyncdownloader.zip
在這個教程中,我將展示利用NSOperation和NSOperationQueue怎么來控制后台的線程。這個教程的目的僅僅是用來演示這些類怎么使用的,所以並不是使用這些類的唯一方法。
如果你熟悉java語言的話。NSOperation對象很像java.lang.Runnable這個接口。就像java.lang.Runnable一樣,NSOperation對象也被設計成可以擴展的。在java的Runnable中,有一個最低級的重載的方法。在NSOperation中此方法是 -(void)main。使用NSOperation最簡單的方法是放置它到NSOperationQueue中。只要該操作被導入到隊列中,此隊列就立刻取得該操作,然后執行它。然后此操作完成,隊列就釋放它。
NSOperation 的例子
這個例子中,我寫了一個NSOperation來抓取一個網頁的字符串,然后把這些字符串放到NSXMLDocument中解析,然后在此操作完成之前,傳遞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
就像你看到的,這個類非常的簡單。初始化的時候,它接受了一個URL,並且存儲了這個URL。當main方法被調用時,它從URL中構造了一個字符串,然后傳遞這個字符串傳給NSXMLDocument初始化。假如在裝載xml文檔的時候,沒有錯誤發生,它將回傳給AppDelegate,在主線程上,然后此任務完成。當main方法結束時,NSOperation也會在隊列中被釋放。
- AppDelegate.h
- #import <Cocoa/Cocoa.h>
- @interface AppDelegate : NSObject {
- NSOperationQueue *queue;
- }
- + (id)shared;
- - (void)pageLoaded:(NSXMLDocument*)document;
- @end
- #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中,兩件事正在發生。第一,在初始化方法中,NSOperationQueue裝載一些URL數組。然后當應用程序完成裝載的時候,也就是在被應用程序實例調用的applicationDidFinishLaunching方法中,通過url數組循環,為每個url創建一個任務,然后放置這些任務到NSOperationQueue中。只要任何一個NSOperation被安排到隊列中,就回立刻被隊列獲取,然后分配它到一個NSThread中,然后NSThread就會運行NSOperation中的main函數中的方法。一旦操作完成,線程就報告給隊列,然后隊列就釋放這個操作。
NSOperationQueue同步
在這個簡單的例子中,很困那導入足夠多的對象,使之並行運行。然而,如果你運行的任務需要花費大量的時間,你將會看到此隊列同時運行很多任務。幸運的是,如果你想降低並發任務的數量,你能在AppDelegate的初始化方法中,
如下的設置:
- - (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;
- }
這樣的話,在相同的時間,隊列就會被控制在只有2個操作在運行。剩下的操作將會等待,直到頭兩個操作被執行完,然后隊列才能得到機會去執行其他的操作,直到隊列空為止。
總結
這些是NSOperation和NSOperationQueue最基礎的操作。你會注意到代碼中很多地方都與NSOperation,NSOperationQueue的建立和使用沒有關系。事實上使用NSOperation和NSOperationQueue的代碼是驚人的小。然而,使用如此少的代碼,你就可以在程序中使用多線程,所以它提供了一個很好的方式方便的管理應用程序中復雜的任務。