ios 之網(wǎng)絡(luò)知識(shí)
一:確認(rèn)網(wǎng)絡(luò)環(huán)境3G/WIFI
1. 添加源文件和framework
開發(fā)Web等網(wǎng)絡(luò)應(yīng)用程序的時(shí)候,需要確認(rèn)網(wǎng)絡(luò)環(huán)境,連接情況等信息。如果沒(méi)有處理它們,是不會(huì)通過(guò)Apple的審(我們的)查的。
Apple 的 例程 Reachability 中介紹了取得/檢測(cè)網(wǎng)絡(luò)狀態(tài)的方法。要在應(yīng)用程序程序中使用Reachability,首先要完成如下兩部:
1.1. 添加源文件:
在你的程序中使用 Reachability 只須將該例程中的 Reachability.h 和 Reachability.m 拷貝到你的工程中。如下圖:
1.2.添加framework:
將SystemConfiguration.framework 添加進(jìn)工程。如下圖:
2. 網(wǎng)絡(luò)狀態(tài)
Reachability.h中定義了三種網(wǎng)絡(luò)狀態(tài):
typedef enum {
NotReachable = 0, //無(wú)連接
ReachableViaWiFi, //使用3G/GPRS網(wǎng)絡(luò)
ReachableViaWWAN //使用WiFi網(wǎng)絡(luò)
} NetworkStatus;
因此可以這樣檢查網(wǎng)絡(luò)狀態(tài):
Reachability *r = [Reachability reachabilityWithHostName:@“www.apple.com”];
switch ([r currentReachabilityStatus]) {
case NotReachable:
// 沒(méi)有網(wǎng)絡(luò)連接
break;
case ReachableViaWWAN:
// 使用3G網(wǎng)絡(luò)
break;
case ReachableViaWiFi:
// 使用WiFi網(wǎng)絡(luò)
break;
}
3.檢查當(dāng)前網(wǎng)絡(luò)環(huán)境
程序啟動(dòng)時(shí),如果想檢測(cè)可用的網(wǎng)絡(luò)環(huán)境,可以像這樣
// 是否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. 鏈接狀態(tài)的實(shí)時(shí)通知
網(wǎng)絡(luò)連接狀態(tài)的實(shí)時(shí)檢查,通知在網(wǎng)絡(luò)應(yīng)用中也是十分必要的。接續(xù)狀態(tài)發(fā)生變化時(shí),需要及時(shí)地通知用戶:
Reachability 1.5版本
// My.AppDelegate.h
#import "Reachability.h"
@interface MyAppDelegate : NSObject
NetworkStatus remoteHostStatus;
}
@property NetworkStatus remoteHostStatus;
@end
// My.AppDelegate.m
#import "MyAppDelegate.h"
@implementation MyAppDelegate
@synthesize remoteHostStatus;
// 更新網(wǎng)絡(luò)狀態(tài)
- (void)updateStatus {
self.remoteHostStatus = [[Reachability sharedReachability] remoteHostStatus];
}
// 通知網(wǎng)絡(luò)狀態(tài)
- (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];
}
}
// 程序啟動(dòng)器,啟動(dòng)網(wǎng)絡(luò)監(jiān)視
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// 設(shè)置網(wǎng)絡(luò)檢測(cè)的站點(diǎn)
[[Reachability sharedReachability] setHostName:@"www.apple.com"];
[[Reachability sharedReachability] setNetworkStatusNotificationsEnabled:YES];
// 設(shè)置網(wǎng)絡(luò)狀態(tài)變化時(shí)的通知函數(shù)
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:)
name:@"kNetworkReachabilityChangedNotification" object:nil];
[self updateStatus];
}
- (void)dealloc {
// 刪除通知對(duì)象
[[NSNotificationCenter defaultCenter] removeObserver:self];
[window release];
[super dealloc];
}
Reachability 2.0版本
// MyAppDelegate.h
@class Reachability;
@interface MyAppDelegate : NSObject
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 {
// ...
// 監(jiān)測(cè)網(wǎng)絡(luò)情況
[[NSNotificationCenter defaultCenter] addObserver:self
[!--empirenews.page--]selector:@selector(reachabilityChanged:)
name: kReachabilityChangedNotification
object: nil];
hostReach = [[Reachability reachabilityWithHostName:@"www.google.com"] retain];
hostReach startNotifer];
// ...
}
二:使用NSConnection下載數(shù)據(jù)
1.創(chuàng)建NSConnection對(duì)象,設(shè)置委托對(duì)象
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. 實(shí)現(xiàn)委托方法
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// store data
[self.receivedData setLength:0]; //通常在這里先清空接受數(shù)據(jù)的緩存
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.receivedData appendData:data]; //可能多次收到數(shù)據(jù),把新的數(shù)據(jù)添加在現(xiàn)有數(shù)據(jù)最后
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// 錯(cuò)誤處理
}
- (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. 設(shè)置委托對(duì)象,開始解析
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data]; //或者也可以使用initWithContentsOfURL直接下載文件,但是有一個(gè)原因不這么做:
// 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. 應(yīng)用舉例
- (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啟動(dòng)多線程
在app store中的很多應(yīng)用程序非常的笨重,他們有好的界面,但操作性很差,比如說(shuō)當(dāng)程序從網(wǎng)上或本地載入數(shù)據(jù)的時(shí)候,界面被凍結(jié)了,用戶只能等程序完全載入數(shù)據(jù)之后才能進(jìn)行操作。
當(dāng)打開一個(gè)應(yīng)用程序時(shí),iphone會(huì)產(chǎn)生一個(gè)包含main方法的線程,所用程序中的界面都是運(yùn)行在這個(gè)線程之中的(table views, tab bars, alerts…),有時(shí)候我們會(huì)用數(shù)據(jù)填充這些view,現(xiàn)在問(wèn) 題是如何有效的載入數(shù)據(jù),并且用戶還能自如的操作程序。方法是啟動(dòng)新的線程,專門用于數(shù)據(jù)的下載,而主線程不會(huì)因?yàn)橄螺d數(shù)據(jù)被阻塞。[!--empirenews.page--]
不管使用任何編程語(yǔ)言,在實(shí)現(xiàn)多線程時(shí)都是一件很麻煩的事情。更糟糕的是,一旦出錯(cuò),這種錯(cuò)誤通常相當(dāng)糟糕。然而,幸運(yùn)的是apple從os x10.5在這方面做了很多的改進(jìn),NSThread的引入,使得開發(fā)多線程應(yīng)用程序容易多了。除此之外,它們還引入了兩個(gè)全新的類,NSOperation和NSOperationQueue。
接下來(lái)我們通過(guò)一個(gè)實(shí)例來(lái)剖析如何使用這兩個(gè)類實(shí)現(xiàn)多線程。這里指示展示這兩個(gè)類的基本用法,當(dāng)然這不是使用他們的唯一辦法。
如果你熟悉java或者它的別的變種語(yǔ)言的話 ,你會(huì)發(fā)現(xiàn)NSOperation對(duì)象很像java.lang.Runnable接口,就像java.lang.Runnable接口那樣,NSOperation類也被設(shè)計(jì)為可擴(kuò)展的,而且只有一個(gè)需要重寫的方法。它就是-(void)main。使用NSOperation的最簡(jiǎn)單的方式就是把一個(gè)NSOperation對(duì)象加入到NSOperationQueue隊(duì)列中,一旦這個(gè)對(duì)象被加入到隊(duì)列,隊(duì)列就開始處理這個(gè)對(duì)象,直到這個(gè)對(duì)象的所有操作完成。然后它被隊(duì)列釋放。
下面的例子中,使用一個(gè)獲取網(wǎng)頁(yè),并對(duì)其解析程N(yùn)SXMLDocument,最后將解析得到的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
正如我們所看到的那樣,這個(gè)類相當(dāng)?shù)暮?jiǎn)單,在它的init方法中接受一個(gè)url并保存起來(lái),當(dāng)main函數(shù)被調(diào)用的時(shí)候,它使用這個(gè)保存的url創(chuàng)建一個(gè)字符串,并將這個(gè)字符串傳遞給NSXMLDocumentinit方法。如果加載的xml數(shù)據(jù)沒(méi)有出錯(cuò),數(shù)據(jù)會(huì)被傳遞給AppDelegate,它處于主線程中。到此,這個(gè)線程的任務(wù)就完成了。在主線程中注銷操作隊(duì)列的時(shí)候,會(huì)將這個(gè)NSOperation對(duì)象釋放。
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)
在上面這個(gè)簡(jiǎn)單的例子中,我們很難看出這些操作是并行運(yùn)行的,然而,如果你你的操作花費(fèi)的時(shí)間遠(yuǎn)遠(yuǎn)比這里的要長(zhǎng),你將會(huì)發(fā)現(xiàn),隊(duì)列是同時(shí)執(zhí)行這些操作的。幸運(yùn)的是,如果你想要為隊(duì)列限制同時(shí)只能運(yùn)行幾個(gè)操作,你可以使用NSOperationQueue的setMaxConcurrentOperationCount:方法。例如,[queue setMaxConcurrentOperationCount:2];