ios網絡編程
一:確認網絡環境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 {
/ appends the new data to the received 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];
當 打開一個應用程序時,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 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
NSOperationQueue的并行控制(NSOperationQueue Concurrency)
在 上面這個簡單的例子中,我們很難看出這些操作是并行運行的,然而,如果你你的操作花費的時間遠遠比這里的要長,你將會發現,隊列是同時執行這些操作的。幸 運的是,如果你想要為隊列限制同時只能運行幾個操作,你可以使用NSOperationQueue的 setMaxConcurrentOperationCount:方法。例如,[queue setMaxConcurrentOperationCount:2];