一:確認網路環境3G/WIFI
1. 添加源檔案和framework 開發Web等網路應用程式的時候,需要確認網路環境,串連情況等資訊。如果沒有處理它們,是不會通過Apple的審(我們的)查的。 Apple 的 常式 Reachability 中介紹了取得/檢測網路狀態的方法。要在應用程式程式中使用Reachability,首先要完成如下兩部: 1.1. 添加源檔案: 在你的程式中使用 Reachability 只須將該常式中的 Reachability.h 和 Reachability.m 拷貝到你的工程中。如: 1.2.添加framework: 將SystemConfiguration.framework 添加進工程。如: 2. 網路狀態 Reachability.h中定義了三種網路狀態: typedef enum { NotReachable = 0, //無串連 ReachableViaWiFi, //使用3G/GPRS網路 ReachableViaWWAN //使用WiFi網路 } NetworkStatus; 因此可以這樣檢查網路狀態: Reachability *r = [Reachability reachabilityWithHostName:@“www.apple.com”]; switch ([r currentReachabilityStatus]) { case NotReachable: // 沒有網路連接 break; case ReachableViaWWAN: // 使用3G網路 break; case ReachableViaWiFi: // 使用WiFi網路 break; } 3.檢查當前網路環境 程式啟動時,如果想檢測可用的網路環境,可以像這樣 // 是否wifi + (BOOL) IsEnableWIFI { return ([[Reachability reachabilityForLocalWiFi] currentReachabilityStatus] != NotReachable); } // 是否3G + (BOOL) IsEnable3G { return ([[Reachability reachabilityForInternetConnection] currentReachabilityStatus] != NotReachable); } 例子: - (void)viewWillAppear:(BOOL)animated { if (([Reachability reachabilityForInternetConnection].currentReachabilityStatus == NotReachable) && ([Reachability reachabilityForLocalWiFi].currentReachabilityStatus == NotReachable)) { self.navigationItem.hidesBackButton = YES; [self.navigationItem setLeftBarButtonItem:nil animated:NO]; } } 4. 連結狀態的即時通知 網路連接狀態的即時檢查,通知在網路應用中也是十分必要的。接續狀態發生變化時,需要及時地通知使用者: Reachability 1.5版本 // My.AppDelegate.h #import "Reachability.h" @interface MyAppDelegate : NSObject <UIApplicationDelegate> { NetworkStatus remoteHostStatus; } @property NetworkStatus remoteHostStatus; @end // My.AppDelegate.m #import "MyAppDelegate.h" @implementation MyAppDelegate @synthesize remoteHostStatus; // 更新網路狀態 - (void)updateStatus { self.remoteHostStatus = [[Reachability sharedReachability] remoteHostStatus]; } // 通知網路狀態 - (void)reachabilityChanged:(NSNotification *)note { [self updateStatus]; if (self.remoteHostStatus == NotReachable) { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"AppName", nil) message:NSLocalizedString (@"NotReachable", nil) delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil]; [alert show]; [alert release]; } } // 程式啟動器,啟動網路監視 - (void)applicationDidFinishLaunching:(UIApplication *)application { // 設定網路檢測的網站 [[Reachability sharedReachability] setHostName:@"www.apple.com"]; [[Reachability sharedReachability] setNetworkStatusNotificationsEnabled:YES]; // 設定網路狀態變化時的通知函數 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:) name:@"kNetworkReachabilityChangedNotification" object:nil]; [self updateStatus]; } - (void)dealloc { // 刪除通知對象 [[NSNotificationCenter defaultCenter] removeObserver:self]; [window release]; [super dealloc]; } Reachability 2.0版本 // MyAppDelegate.h @class Reachability; @interface MyAppDelegate : NSObject <UIApplicationDelegate> { Reachability *hostReach; } @end // MyAppDelegate.m - (void)reachabilityChanged:(NSNotification *)note { Reachability* curReach = [note object]; NSParameterAssert([curReach isKindOfClass: [Reachability class]]); NetworkStatus status = [curReach currentReachabilityStatus]; if (status == NotReachable) { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"AppName"" message:@"NotReachable" delegate:nil cancelButtonTitle:@"YES" otherButtonTitles:nil]; [alert show]; [alert release]; } } - (void)applicationDidFinishLaunching:(UIApplication *)application { // ... // 監測網路情況 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:) name: kReachabilityChangedNotification object: nil]; hostReach = [[Reachability reachabilityWithHostName:@"www.google.com"] retain]; hostReach startNotifer]; // ... } 二:使用NSConnection下載資料 1.建立NSConnection對象,設定委派物件 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[self urlString]]]; [NSURLConnection connectionWithRequest:request delegate:self]; 2. NSURLConnection delegate委託方法 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response; - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error; - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data; - (void)connectionDidFinishLoading:(NSURLConnection *)connection; 3. 實現委託方法 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { // store data [self.receivedData setLength:0]; //通常在這裡先清空接受資料的緩衝 } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [self.receivedData appendData:data]; //可能多次收到資料,把新的資料添加在現有資料最後 } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { // 錯誤處理 } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { // disconnect [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; NSString *returnString = [[NSString alloc] initWithData:self.receivedData encoding:NSUTF8StringEncoding]; NSLog(returnString); [self urlLoaded:[self urlString] data:self.receivedData]; firstTimeDownloaded = YES; } 三:使用NSXMLParser解析xml檔案 1. 設定委派物件,開始解析 NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data]; //或者也可以使用initWithContentsOfURL直接下載檔案,但是有一個原因不這麼做: // It's also possible to have NSXMLParser download the data, by passing it a URL, but this is not desirable // because it gives less control over the network, particularly in responding to connection errors. [parser setDelegate:self]; [parser parse]; 2. 常用的委託方法 - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict; - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName; - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string; - (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError; static NSString *feedURLString = @"http://www.yifeiyang.net/test/test.xml"; 3. 應用舉例 - (void)parseXMLFileAtURL:(NSURL *)URL parseError:(NSError **)error { NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:URL]; [parser setDelegate:self]; [parser setShouldProcessNamespaces:NO]; [parser setShouldReportNamespacePrefixes:NO]; [parser setShouldResolveExternalEntities:NO]; [parser parse]; NSError *parseError = [parser parserError]; if (parseError && error) { *error = parseError; } [parser release]; } - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString*)qName attributes:(NSDictionary *)attributeDict{ // 元素開始控制代碼 if (qName) { elementName = qName; } if ([elementName isEqualToString:@"user"]) { // 輸出屬性值 NSLog(@"Name is %@ , Age is %@", [attributeDict objectForKey:@"name"], [attributeDict objectForKey:@"age"]); } } - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { // 元素終了控制代碼 if (qName) { elementName = qName; } } - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { // 取得元素的text } NSError *parseError = nil; [self parseXMLFileAtURL:[NSURL URLWithString:feedURLString] parseError:&parseError]; 使用NSOperation和NSOperationQueue啟動多線程 在app store中的很多應用程式非常的笨重,他們有好的介面,但操作性很差,比如說當程式從網上或本地載入資料的時候,介面被凍結了,使用者只能等程式完全載入資料之後才能進行操作。當開啟一個應用程式時,iphone會產生一個包含main方法的線程,所用程式中的介面都是運行在這個線程之中的(table views, tab bars, alerts…),有時候我們會用資料填充這些view,現在問 題是如何有效載入資料,並且使用者還能自如的操作程式。方法是啟動新的線程,專門用於資料的下載,而主線程不會因為下載資料被阻塞。不管使用任何程式設計語言,在實現多線程時都是一件很麻煩的事情。更糟糕的是,一旦出錯,這種錯誤通常相當糟糕。然而,幸運的是apple從os x10.5在這方面做了很多的改進,NSThread的引入,使得開發多線程應用程式容易多了。除此之外,它們還引入了兩個全新的類,NSOperation和NSOperationQueue。接下來我們通過一個執行個體來剖析如何使用這兩個類實現多線程。這裡指示展示這兩個類的基本用法,當然這不是使用他們的唯一辦法。如果你熟悉java或者它的別的變種語言的話 ,你會發現NSOperation對象很像java.lang.Runnable介面,就像java.lang.Runnable介面那樣,NSOperation類也被設計為可擴充的,而且只有一個需要重寫的方法。它就是-(void)main。使用NSOperation的最簡單的方式就是把一個NSOperation對象加入到NSOperationQueue隊列中,一旦這個對象被加入到隊列,隊列就開始處理這個對象,直到這個對象的所有操作完成。然後它被隊列釋放。下面的例子中,使用一個擷取網頁,並對其解析程NSXMLDocument,最後將解析得到的NSXMLDocument返回給主線程。 PageLoadOperation.h@interface PageLoadOperation : NSOperation { NSURL *targetURL;}@property(retain) NSURL *targetURL;- (id)initWithURL:(NSURL*)url;@end PageLoadOperation.m#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建立一個字串,並將這個字串傳遞給NSXMLDocumentinit方法。如果載入的xml資料沒有出錯,資料會被傳遞給AppDelegate,它處於主線程中。到此,這個線程的任務就完成了。在主線程中登出操作隊列的時候,會將這個NSOperation對象釋放。AppDelegate.h@interface AppDelegate : NSObject { NSOperationQueue *queue;}+ (id)shared;- (void)pageLoaded:(NSXMLDocument*)document;@endAppDelegate.m #import "AppDelegate.h"#import "PageLoadOperation.h"@implementation AppDelegatestatic 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 NSOperationQueue的並行控制(NSOperationQueue Concurrency) 在上面這個簡單的例子中,我們很難看出這些操作是並行啟動並執行,然而,如果你你的操作花費的時間遠遠比這裡的要長,你將會發現,隊列是同時執行這些操作的。幸運的是,如果你想要為隊列限制同時只能運行幾個操作,你可以使用NSOperationQueue的setMaxConcurrentOperationCount:方法。例如,[queue setMaxConcurrentOperationCount:2];