[分享]iOS開發 - 網絡總結

基本概念html

客戶端:client
服務器:server
請求:request
響應:response
過程ios

客戶端 -> 發送請求 -> 服務器(鏈接數據庫)
服務器 -> 發送響應 -> 客戶端
客戶端(移動端)web

前段(前臺)
iOS,Android
服務器(後端)數據庫

後臺
Java、PHP、.NET
遠程服務器-面向全部用戶(上線)
本地服務器-面向公司內部(測試)
URLjson

URL的全稱是Uniform Resource Locator(統一資源定位符)
經過1個URL,能找到互聯網上惟一的1個資源
URL就是資源的地址、位置,互聯網上的每一個資源都有一個惟一的URL
URL的基本格式 = 協議://主機地址/路徑
URL中常見協議後端

HTTP - 網絡全部資源( http://)這個最經常使用
file - 本機資源( file://)
mailto - 郵件地址 (mailto:)
FTP - 網絡文件資源 ( ftp://)數組

HTTP通訊緩存

http的做用是統一客戶端和服務器的數據交換格式,使得彼此能夠理解。
優勢安全

簡單快速,服務器規模小,通訊速度快
靈活,容許傳輸任意類型的數據
HTTP 0.9和1.0使用短鏈接方式(非持續鏈接):每次鏈接只處理一個請求,服務器作出響應後,立刻斷開鏈接。服務器

iOS中經常使用的HTTP請求方式

原生

NSURLConnection - 最古老的方案
NSURLSession - iOS7推出的新技術
CFNetworking - NSURL的底層,純C語言
第三方框架

ASIHttpRequest - 外號「HTTP終結者」,功能強大,惋惜已經中止更新
AFNetworking - 維護使用者多
MKNetworkKit - 印度,維護使用者少
HTTP 請求

HTTP/1.1 中定義了8種方法
GET 、 POST 、OPTIONS、HEAD、PUT、DELETE、TRACE、CONNECT、PATCH
最經常使用的就是GET、POST

HTTP 實踐 - NSURLConnection (瞭解便可)

HTTP同步請求

// 發送同步請求,會一直等待, 直到接收到數據
-(void)requestSynch
{
  // 1 建立請求鏈接
  NSURL *url = [NSURL URLWithString:@"http://www.lala.com/login?username=123&pwd=123"];
  // 2 建立請求
  NSURLRequest *request = [NSURLRequest requestWithURL:url ];
  NSHTTPURLResponse *response = nil;
  NSError *error = nil;
  // 3 發送同步請求
  // endSynchronousRequest阻塞式的方法,等待服務器返回數據
  NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
  // 4.解析服務器返回的數據(解析成字符串)
  NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  NSLog(@"--%@--%@-- %zd",str,response.allHeaderFields,response.statusCode);
}

HTTP異步請求
// 發送異步請求

-(void)requestAsync
{
  // 1 建立請求鏈接
  NSURL *url = [NSURL URLWithString:@"http://123.123.123.123/login?username=123&pwd=123"];
  // 2 建立請求
  NSURLRequest *request = [NSURLRequest requestWithURL:url ];
  // 3 發送異步請求
  [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
    // 4.解析服務器返回的數據(解析成字符串)
    NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSHTTPURLResponse *httpRes = (NSHTTPURLResponse* )response;
    NSLog(@"--%@--%zd--%@--",str,httpRes.statusCode,httpRes.allHeaderFields);
  }];
}

HTTP代理請求模式

協議: NSURLConnectionDataDelegate
實現方法

// 1 建立請求鏈接
  NSURL *url = [NSURL URLWithString:@"http://123.123.123.123/login?username=520it&pwd=520it"];
  // 2 建立請求
  NSURLRequest *request = [NSURLRequest requestWithURL:url ];
  // 3 代理請求模式,要遵照協議並實現代理方法
  [NSURLConnection connectionWithRequest:request delegate:self];
///-----------------------------------------------------------------///
// 經常使用代理方法
// 接收服務器響應
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
  self.localData = [NSMutableData data];
   NSLog(@"-didReceiveResponse-%zd",((NSHTTPURLResponse *)response).statusCode);
}
// 接收到服務器的數據(若是數據量比較大,這個方法會被調用屢次)
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
  [self.localData appendData:data];
    NSLog(@"-didReceiveData-");
}
// 完成數據下載
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
  NSString *str = [[NSString alloc] initWithData:self.localData encoding:NSUTF8StringEncoding];
  NSLog(@"-connectionDidFinishLoading-%@",str);
}
// 請求失敗:請求超時等
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
   NSLog(@"-didFailWithError-");
}

簡單登錄界面

GET請求

-(IBAction)loginBtn:(id)sender {
// 獲取控件數據和網絡數據進行比對
NSString *userName = self.userText.text;
if (userName.length == 0 ) {
  [SVProgressHUD showErrorWithStatus:@"用戶名不能爲空"];
  return;
}
NSString *pwd = self.pwdText.text;
if (pwd.length == 0) {
  [SVProgressHUD showErrorWithStatus:@"密碼不能爲空"];
  return;
}
// 顯示陰影
[SVProgressHUD showWithStatus:@"正在登錄中" maskType:SVProgressHUDMaskTypeBlack];
//
NSString *format = [NSString stringWithFormat:@"http://123.123.123.123/login?username=%@&pwd=%@",userName,pwd];
NSLog(@"%@",format);
// 1 建立請求鏈接
NSURL *url = [NSURL URLWithString:format];
// 2 建立請求
NSURLRequest *request = [NSURLRequest requestWithURL:url ];
// 3 發送異步請求
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
  // JSON:{"success":"登陸成功"}
  // 4.解析服務器返回的數據(解析成字符串)
  NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  // 對字符串進行分割
  NSInteger loc = [str rangeOfString:@"\":\""].location + 3;
  NSInteger len = [str rangeOfString:@"\"}"].location - loc;
  NSString *result = [str substringWithRange:NSMakeRange(loc, len)];
  // 顯示結果
  if ([result containsString:@"success"]) {
    [SVProgressHUD showSuccessWithStatus:result];
  }
  else
  {
    [SVProgressHUD showErrorWithStatus:result];
  }
}];

POST請求

NSString *format = [NSString stringWithFormat:@"http://123.123.123.123/login"];
  // 1 建立請求鏈接
  NSURL *url = [NSURL URLWithString:format];
  // 2 建立請求
  NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url ];
  // 更改請求方法
  request.HTTPMethod = @"POST";
  // 設置請求體
  request.HTTPBody = [@"username=520it&pwd=520it" dataUsingEncoding:NSUTF8StringEncoding];
  // 設置超時
  request.timeoutInterval = 5;
  // 設置請求頭
//    [request setValue:@"ios9.0" forHTTPHeaderField:@"User-agent"];
  // 3 發送請求
  [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
    if (connectionError) {
      NSLog(@"失敗");
    }
    else
    {
      NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }
  }];

路徑中有漢字的話必須進行轉換
GET:手動轉換

format = [format stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
POST:在設置請求體時直接設置了編碼格式,會自動轉換
// 設置請求體
    request.HTTPBody = [@"username=小碼哥&pwd=520it" dataUsingEncoding:NSUTF8StringEncoding];

解析JSON

服務器返回給客戶端的數據通常都是JSON或者XML
JSON的格式很像OC中的字典和數組

{"name" : "jack", "age" : 10}
{"names" : ["jack", "rose", "jim"]}
標準JSON格式的注意點:key必須用雙引號
JSON和OC的對應關係
圖片描述

IOS中JSON解決方案

  • 第三方

  • JSONKit、SBJson、TouchJSON(性能從左到右,越差)

  • 蘋果原生(自帶):NSJSONSerialization(性能最好)

NSJSONSerialization

  • 解析JSON

  • data轉JSON

    -(id)JSONObjectWithData:(NSData )data options:(NSJSONReadingOptions)opt error:(NSError *)error;

    // data轉JSON

    NSString *str = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions
  • JSON轉data

    -(NSData )dataWithJSONObject:(id)obj options:(NSJSONWritingOptions)opt error:(NSError *)error;

  • 關於參數

    typedef NS_OPTIONS(NSUInteger, NSJSONReadingOptions) {

    NSJSONReadingMutableContainers = (1UL << 0), // 可變容器
    NSJSONReadingMutableLeaves = (1UL << 1), // 子節點也是可變的,也就是說轉換的全部數據都是可變的
    NSJSONReadingAllowFragments = (1UL << 2) // 接收零散數據,好比說單個的‘10’,'false'

    } NS_ENUM_AVAILABLE(10_7, 5_0);

  • 參數 NSJSONReadingAllowFragments 使用以下

    // 參數NSJSONReadingAllowFragments 用來讀取單個元素
    NSString *str  = @"10";
    NSData *data = [NSJSONSerialization JSONObjectWithData:[str dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil];
    NSLog(@"-- %@ --",data);
  • JSON轉模型

  • 每次都手動去轉換的話,很是費時費力。可使用第三方框架

  • MJExtension

    #import <MJExtension.h>
    // 得到視頻的模型數組
    self.videos = [SLQVideo objectArrayWithKeyValuesArray:dict[@"videos"]];

    蘋果自帶的movie播放器

    只能播放有限的幾種格式(mp四、mov等)
    // 播放視頻

    NSURL *url = [NSURL URLWithString:@"http://123.123.123.123"];
    // MPMoviePlayerViewController // 視圖控制器
    // MPMoviePlayerController // 內部沒有view,不能直接彈出
    MPMoviePlayerViewController *vc = [[MPMoviePlayerViewController alloc] initWithContentURL:[url URLByAppendingPathComponent:video[@"url"]]];
    // modal 出視頻並播放
     [self presentViewController:vc animated:YES completion:nil];

字典轉模型框架

  • Mantle

全部模型都必須繼承自MTModel

  • JSONModel

全部模型都必須繼承自JSONModel

  • MJExtension

不須要強制繼承任何其餘類

設計框架須要考慮的問題

  • 侵入性

侵入性大就意味着很難離開這個框架

  • 易用性

好比少許代碼實現N多功能

  • 擴展性

很容易給這個框架增長新框架

解析XML

  • XML解析方式

SAX:逐行解釋
DOM:一次性加載到內存

  • 蘋果自帶:NSXMLParser
    SAX 方式

  • 第三方庫

1.libxml2 :純C語言,默認包含在iOS SDK 中,同時支持SAX、DOM
2.GDataXML :DOM方式,由google開發,基於libxml2

  • 建議
    大文件 : NSXMLParser、libxml2

小文件 : GDataXML、XSXMLParser、libxml2

XSXMLParser

  • 使用過程

一、設置源,初始化一個XSXMLParser對象
二、設置代理,實現代理方法
三、啓動掃描
// 初始化方法

-(instancetype)initWithContentsOfURL:(NSURL *)url;  // initializes the parser with the specified URL.
-(instancetype)initWithData:(NSData *)data; // create the parser from data
  • 使用方法

    // 1 使用蘋果的XML類解析xml文檔
    NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
    parser.delegate = self; // 2 設置代理
    [parser parse]; // 3 啓動掃描

  • 主要是代理方法的使用

#pragma mark -NSXMLParserDelegate代理方法
// 讀取文檔開始
-(void)parserDidStartDocument:(NSXMLParser *)parser
{
//    NSLog(@"parserDidStartDocument");
//    self.videoArray = [NSMutableArray array];
}
// 讀取文檔結束
-(void)parserDidEndDocument:(NSXMLParser *)parser
{
//    NSLog(@"parserDidEndDocument--%@",self.videoArray);
}
// 開始讀取元素,attributes 參數表示要讀取的元素
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
  //    NSLog(@"didStartElement---%@",attributeDict);
  // 全部的元素都會通過這裏進行讀取,包括根
  // 因此要進行判斷,把根給排除
  if ([elementName isEqualToString:@"videos"]) {
    return;
  }
  // 獲取模型數據,放入數組
  SLQVideoItem *video = [SLQVideoItem objectWithKeyValues:attributeDict];
  [self.videoArray addObject:video];
}
// 讀取元素結束
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
}

格式化服務器返回的JSON數據

在線格式化: http://tool.oschina.net/codef...
將服務器返回的字典或者數組寫成plist文件

GDataXML

這個配置起來很麻煩

首先不支持CocoaPods,因此只能手動拖拽進項目,並且項目比較舊。
而後進入工程設置 Header Search Paths 添加 /usr/include/libxml2
設置工程 Other Linker Flags contain 添加 -lxml2
改變文件的編譯方式爲非ARC - -fno -objc -arc
最後編譯纔不會出錯
使用過程

GDataXMLDocument - 整個文檔
GDataXMLElement - xml中某個元素

// 1 使用GDataXML解析xml文件
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:data options:0 error:nil];
// 2 獲取全部video元素,首先要得到根節點
NSArray *elements = [doc.rootElement elementsForName:@"video"];
// 3 遍歷全部元素
for (GDataXMLElement *ele in elements) {
  SLQVideoItem *video  = [[SLQVideoItem alloc] init];
  // 4 獲取元素屬性,轉換成字符串
  video.name = [[ele attributeForName:@"name"] stringValue];
  video.image  = [[ele attributeForName:@"image"] stringValue];
  video.url = [[ele attributeForName:@"url"] stringValue];
  video.length = [[[ele attributeForName:@"length"] stringValue] intValue];
  [self.videoArray addObject:video];
}

多值參數

同一個參數傳如多個數據,逗號分隔
一個參數包括多個字典

// NSURL *url = [NSURL URLWithString:@"http://123.123.123.123/weather?place=beijing&place=shanghai"];
    NSURL *url = [NSURL URLWithString:@"http://123.123.123.123/weather?place=beijing,shanghai"];

解決輸出到控制檯顯示中文亂碼的問題

重寫descriptionWithLocale方法,這二個方法會在轉換JSON時自動調用。

@implementation NSDictionary (Log)
-(NSString *)descriptionWithLocale:(id)locale
{
  // {"weathers":[{"city":"beijing,shanghai","status":"晴轉多雲"}]}\
  // 將這句話格式化輸出
  NSMutableString *str =[NSMutableString string];
  [str appendString:@"{\n"];
  [self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    // 拼接字符串
    [str appendFormat:@"\t%@",key];
    [str appendString:@" : "];
    [str appendFormat:@"%@,\n",obj];
  }];
  [str appendString:@"}"];
  // 取出最後一個逗號
  NSRange range = [str rangeOfString:@"," options:NSBackwardsSearch];
  if (range.location != NSNotFound) {
    [str deleteCharactersInRange:range];
  }
  return str;
}
@end
/*
 2015-07-15 18:43:08.137 05-掌握-多值參數[65936:116425]
 {
  weathers : [
  {
  status : 晴轉多雲,
  city : Beijing
 },
  {
  status : 晴轉多雲,
  city : Shanghai
 }
 ]
 }
 */
@implementation NSArray (Log)
-(NSString *)descriptionWithLocale:(id)locale
{
  // 將這句話格式化輸出
  NSMutableString *str =[NSMutableString string];
  [str appendString:@"[\n"];
  [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    [str appendFormat:@"\t%@,\n",obj];
  }];
  [str appendString:@"\t]"];
  // 取出最後一個逗號
  NSRange range = [str rangeOfString:@"," options:NSBackwardsSearch];
  if (range.location != NSNotFound) {
    [str deleteCharactersInRange:range];
  }
  return str;
}
@end

文件下載

小文件下載

直接使用NSData下載

NSData *data = [NSData dataWithContentsOfURL:url]; 
使用NSURLConnection 異步鏈接

NSURL *url  = [NSURL URLWithString:@"http://123.123.123.123/resources/images/minion_13.png"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
    NSLog(@"---%zd---",data.length);
}];

使用NSURLConnection代理方式實現

-(void)viewDidLoad {
  [super viewDidLoad];
  // 創建鏈接
  NSURL *url  = [NSURL URLWithString:@"http://123.123.123.123/resources/videos/minion_02.mp4"];
  [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:url] delegate:self];
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response
{
  NSLog(@"didReceiveResponse:%@",response);
  // 初始化
  self.data = [NSMutableData data];
  self.movieCount = [response.allHeaderFields[@"Content-Length"] integerValue];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
  // 緩存到內存
  [self.data appendData:data];
  CGFloat progress = 1.0 * self.data.length / self.movieCount;
  self.progressView.progress = progress;
  NSLog(@"%f",progress * 100 );
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
  // 這裏進行文件的保存,保存到cache裏
  NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
  [self.data writeToFile:[filePath stringByAppendingPathComponent:@"1.mp4"] atomically:YES];
  self.data = nil;
}

大文件下載

使用NSURLConnection代理方式實現

// 接收到響應的時候:建立一個空的文件
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response
{
  // 獲取文件長度
  self.movieCount = [response.allHeaderFields[@"Content-Length"] integerValue];
  // 建立一個空文件
  [[NSFileManager defaultManager] createFileAtPath:SLQFilePath contents:nil attributes:nil];
  // 建立文件句柄
  self.handle = [NSFileHandle fileHandleForWritingAtPath:SLQFilePath];
 }
// 接收到具體數據:立刻把數據寫入一開始建立好的文件
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
  // 移動到文件末尾
  [self.handle seekToEndOfFile];
  // 寫入數據
  [self.handle writeData:data];
  //
  self.currentCount += data.length;
  CGFloat progress = 1.0 * self.currentCount / self.movieCount;
  self.progressView.progress = progress;
  NSLog(@"%f",progress * 100 );
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
  self.movieCount = 0;
  // 關閉文件
  [self.handle closeFile];
  self.handle = nil;
}

解壓縮

解壓縮使用第三方庫 SSZipArchive

// 1 使用指定文件,建立一個壓縮文件
    NSArray *paths = @[
      @"/Users/song/Desktop/test/1.png",
      @"/Users/song/Desktop/test/2.png",
      @"/Users/song/Desktop/test/3.png",
      @"/Users/song/Desktop/test/4.png",
      @"/Users/song/Desktop/test/5.png"
      ];
    [Main createZipFileAtPath:@"/Users/song/Desktop/test.zip" withFilesAtPaths:paths];
  // 2 使用指定目錄建立一個壓縮文件
    [Main createZipFileAtPath:@"/Users/song/Desktop/test121212.zip" withContentsOfDirectory:@"/Users/song/Desktop/test"];
    // 3 解壓縮一個文件到指定目錄
    [Main unzipFileAtPath:@"/Users/song/Desktop/test.zip" toDestination:@"/Users/song/Desktop"];

文件上傳

// 必定要注意這個格式是固定的
/* 文件參數格式
 --分割線\r\n
 Content-Disposition: form-data; name="參數名"; filename="文件名"\r\n
 Content-Type: 文件的MIMEType\r\n
 \r\n
 文件數據
 \r\n
 */
// 文件上傳
// 一、建立請求
NSURL *url = [NSURL URLWithString:@"http://123.123.123.123/upload"];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 二、設置協議
request.HTTPMethod = @"POST";
// 三、設置請求頭
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",SLQBoundary] forHTTPHeaderField:@"Content-Type"];

// 四、設置請求體
NSMutableData *body = [NSMutableData data];

// 設置文件參數
// 設置邊界
[body appendData:SLQUTF(@"--")];
[body appendData:SLQUTF(SLQBoundary)];
[body appendData:SLQEnter];
// 文件參數名
[body appendData:SLQUTF([NSString[] stringWithFormat:@"Content-Disposition: form-data; name=\"file\"; filename=\"1.png\""])];
[body appendData:SLQEnter];
// 文件類型
[body appendData:SLQUTF([NSString stringWithFormat:@"Content-Type: image/png"])];
[body appendData:SLQEnter];
// 文件內容
[body appendData:SLQEnter];
UIImage *image = [UIImage imageNamed:@"1"];
[body appendData:UIImagePNGRepresentation(image)];
[body appendData:SLQEnter];

/* 非文件參數格式
 --分割線\r\n
 Content-Disposition: form-data; name="參數名"\r\n
 \r\n
 參數值
 \r\n
 */
// 設置非文件參數
[body appendData:SLQUTF(@"--")];
[body appendData:SLQUTF(SLQBoundary)];
[body appendData:SLQEnter];

[body appendData:SLQUTF([NSString stringWithFormat:@"Content-Disposition: form-data; name=\"username\""])];
[body appendData:SLQEnter];

[body appendData:SLQEnter];
[body appendData:SLQUTF(@"bulabulabula")];
[body appendData:SLQEnter];

/* 結束標記
 --分割--線\r\n
 \r\n
 */
// 結束標記
[body appendData:SLQUTF(@"--")];
[body appendData:SLQUTF(SLQBoundary)];
[body appendData:SLQUTF(@"--")];
[body appendData:SLQEnter];


request.HTTPBody = body;


[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
    //  這個方法會調用descriptionWithLocale方法,能夠在這裏解決輸出到控制檯顯示中文亂碼的問題
    NSLog(@"%@",[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
}];

獲取MIMEType

OC 方法

-(NSString *)getMIMEType:(NSString *)path
{
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL fileURLWithPath:path]];
    NSURLResponse *response = nil;
    [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
        return response.MIMEType;
}

c語言

// 要包含頭文件MobileCoreServices.h
+(NSString *)mimeTypeForFileAtPath:(NSString *)path
{
  if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
    return nil;
  }
  CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[path pathExtension], NULL);
  CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass (UTI, kUTTagClassMIMEType);
  CFRelease(UTI);
  if (!MIMEType) {
    return @"application/octet-stream";
  }
  return (__bridge NSString *)(MIMEType);
}

NSOutputStream

文件流,文件輸出流,能夠輸出到內存、硬盤、NSData

-(void)viewDidLoad {
  [super viewDidLoad];
  // 創建鏈接
  NSURL *url  = [NSURL URLWithString:@"http://123.123.123.123/resources/videos/minion_02.mp4"];
  [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:url] delegate:self];
}

 //接收到響應的時候:建立一個空的文件

-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response
{
  // 獲取服務器那裏給出的建議名字   response.suggestedFilename);
  NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:response.suggestedFilename];
  // 建立文件流
  self.stream = [[NSOutputStream alloc] initToFileAtPath:path append:YES];
  // 打開文件流
  [self.stream open];
}

 //接收到具體數據:立刻把數據寫入一開始建立好的文件

-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
  // 參數1要求是bytes
  [self.stream write:[data bytes]  maxLength:data.length];
  NSLog(@"---");
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
  // 關閉文件流
  [self.stream close];
}

NSURLConnection和NSRunLoop

使用NSURLConnection建立的請求,其內部和NSRunLoop有關聯,必須保證NSRunLoop處於運行狀態,不然代理方法運行起來就會出問題。
若是要在子線程裏建立請求,必需要手動啓動NSRunLoop,而且要在方法使用結束手動關閉NSRunLoop。

// 創建鏈接
NSURL *url  = [NSURL URLWithString:@"http://123.123.123.123/resources/images/minion_05.png"];
NSURLConnection *con = [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:url] delegate:self];
// 決定代理方法在哪一個隊列中執行
[con setDelegateQueue:[[NSOperationQueue alloc] init]];
// 開啓子線程runloop
self.runloop =  CFRunLoopGetCurrent();
CFRunLoopRun();

在代理方法中使用完畢,中止runloop

-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"--connectionDidFinishLoading--%@",[NSThread currentThread]);
    CFRunLoopStop(self.runloop);
}

NSURLSession

這個是在iOS7以後推出的用於替代NSURLConnection的新類, 推薦掌握這個 。
NSURLSession 主要由兩部分組成,一個是Session實例對象,一個是任務。
使用步驟

建立task( dataTaskWithRequest ),啓動task( resume )
抽象類,使用其子類( NSURLSessionDataTask 、 NSURLSessionDownloadTask 、 NSURLSessionUploadTask )
大小文件都同樣,默認寫入到tmp目錄下面,下載完畢後要本身移動文件
NSURLSession 代理

初始化時設置代理 <NSURLSessionDataDelegate>
實現過程

接收響應,指定響應方式:取消、下載、變爲下載 didReceivedResponse
接收數據 didReceivedData
接收完畢(成功和失敗都會進入這個方法) didComplete
這個能夠實現大文件下載
大文件斷點下載

NSURLSession 的方法: suspend、resume、cancel
resumeData 保存暫停時程序數據狀態,取消任務後要根據狀態恢復下載
(很差 ,實現複雜)將下載的tmp文件保存到cachae,而後恢復如今時再從cache移動到臨時文件
下載失敗後要從NSError中獲取失敗時的恢復數據
何爲斷點下載

程序因意外事件終止,致使下載被中止,主要考慮用戶忽然關閉程序。
能夠將下載的數據同步到沙盒中,而且記錄下載大小,以及記錄已下載的文件,每次下載以前都進行掃描一番。
若是下載一半就在請求頭裏指定要下載的範圍
上傳

NSURLSessionUploadTast
POST 請求設置注意 methodBody 爲上傳的參數 fromData
NSURLSessionConfiguration 統一配置(好比能夠在這裏設置是否容許設置程序使用蜂窩移動網絡)

GET

NSURL *url  = [NSURL URLWithString:@"http://123.123.123.123/login?username=123&pwd=4324"];
// 1 得到NSURLSession單例對象
NSURLSession *session = [NSURLSession sharedSession];
// 2 建立任務
NSURLSessionDataTask *task = [session dataTaskWithRequest:[NSURLRequest requestWithURL:url] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    // 4 處理數據
    NSLog(@"----%@",[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
}];
// 3 啓動任務
[task resume];

POST

// post請求
NSURL *url  = [NSURL URLWithString:@"http://123.123.123.123/login?username=123&pwd=4324"];
// 得到NSURLSession單例對象
NSURLSession *session = [NSURLSession sharedSession];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 設置請求頭
request.HTTPMethod = @"POST";
// 設置請求體
NSMutableData *body  = [NSMutableData data];
[body appendData:[@"username=123&pwd=234" dataUsingEncoding:NSUTF8StringEncoding]];
request.HTTPBody = body;
// 建立任務
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    NSLog(@"----%@",[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
}];
// 啓動任務
[task resume];

下載

直接使用 downloadTaskWithURL:url 進行下載,不過下載成功的文件是放在tmp臨時目錄裏面的,必定要及時把文件給移出來。

NSURL *url  = [NSURL URLWithString:@"http://123.123.123.123/resources/videos/minion_02.mp4"];
// 得到NSURLSession單例對象
NSURLSession *session = [NSURLSession sharedSession];
// 建立任務
NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
  NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:response.suggestedFilename];
  // 這個方式下載的文件在tmp文件夾下,要把下載的文件移動到cache中永久保存,參數是 fileURLWithPath,看清了
  // loaction 表示在本地臨時存儲區的路徑
  [[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:filePath] error:nil];
}];

// 啓動任務
[task resume];

代理方式

-(void)viewDidLoad {
  [super viewDidLoad];
  NSURL *url  = [NSURL URLWithString:@"http://123.123.123.123/resources/videos/minion_02.mp4"];
  // 建立
  NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
  // 建立任務
  NSURLSessionDataTask *task = [session dataTaskWithURL:url];
  // 啓動任務
  [task resume];
}
// 接收服務器響應,必須手動執行後續的執行方式(容許接收數據仍是不容許接收)
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
  NSLog(@"%s",__func__);
  // 必須手動指定接下來的數據要不要接收
  completionHandler(NSURLSessionResponseAllow);
}
// 接收數據,屢次調用
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
  NSLog(@"%s",__func__);
}
// 下載完畢後調用,若是失敗,看參數error的值
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
  NSLog(@"%s",__func__);
}
//1.是沒用的,由於segment fault會將typedef識別爲1
1.typedef NS_ENUM(NSInteger, NSURLSessionResponseDisposition) {
  NSURLSessionResponseCancel = 0,/* Cancel the load, this is the same as -[task cancel] */
  NSURLSessionResponseAllow = 1,/* Allow the load to continue */
  NSURLSessionResponseBecomeDownload = 2,/* Turn this request into a download */
} NS_ENUM_AVAILABLE(NSURLSESSION_AVAILABLE, 7_0);

斷點下載

經過自定義 NSURLSession ,使用dataWithTask來進行下載,而且手動控制器下載文件的去向。
每次下載以前都要去沙盒讀取已下載的文件,用於判斷從哪裏進行下載
主要方法以下:

-(NSURLSessionDataTask *)dataTask
{
  if (!_dataTask) {
    // 獲取下載進度,直接從沙盒中讀取文件長度
    NSInteger total = [[NSMutableDictionary dictionaryWithContentsOfFile:SLQDownloadFilePath][SLQFileName] integerValue];
    NSInteger current = [[[NSFileManager defaultManager] attributesOfItemAtPath:SLQFilePath error:nil][NSFileSize] integerValue];
    if (total && total == current ) {
      NSLog(@"已經下載完畢");
      return nil;
    }
    // 若是長度和
    NSURL *url  = [NSURL URLWithString:@"http://123.123.123.123/resources/videos/minion_02.mp4"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    // 指定要從服務器獲取的數據的範圍
    NSString *range = [NSString stringWithFormat:@"bytes=%zd-",current];
    [request setValue:range forHTTPHeaderField:@"Range"];
    // 建立任務
    _dataTask = [self.session dataTaskWithRequest:request];
  }
  return  _dataTask;
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSHTTPURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
//    NSLog(@"----%@",[response class]);
  // 開啓輸出流
  [self.stream open];
  // 計算總得數據長度,response會返回請求的數據長度,不包括已經下載的數據長度,因此要累加起來
  self.totalCount = [response.allHeaderFields[@"Content-Length"] integerValue] + SLQFileLength;
  NSString *name = response.suggestedFilename;
  // 存儲總長度,將所要下載的文件長度保存起來
  NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:SLQDownloadFilePath];
  if (dict == nil) {
    dict = [NSMutableDictionary dictionary];
  }
  dict[SLQFileName] = @(self.totalCount);
  [dict writeToFile:SLQDownloadFilePath atomically:YES];
  // 繼續下載
  completionHandler(NSURLSessionResponseAllow);
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
  // 寫入數據到沙盒
  [self.stream write:[data bytes]maxLength:data.length];
  // 獲取下載進度,直接從沙盒中讀取文件長度
  NSLog(@"---%f",1.0 * SLQFileLength / self.totalCount);
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
  // 清空
  self.dataTask = nil;
  [self.stream close];
  self.stream = nil;
}

NSURLSession 上傳文件

  • 必須按照格式寫,一個空格或者回車都不能多。

    // 必定要注意這個格式是固定的

    /* 文件參數格式

    --分割線\r\n
    Content-Disposition: form-data; name="file"; filename="文件名"\r\n
    Content-Type: 文件的MIMEType\r\n
    \r\n
    文件數據
    \r\n
    // 結束標記
    \r\n
    --分割線--\r\n
    \r\n
    */
    // 主要是參數第二個參數要傳入 **`請求體`**
      [[self.session uploadTaskWithRequest:request fromData:body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
       NSLog(@"---%@",[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);

    }] resume];

    AFNetworking

  • GETPOST

  • AFHTTPRequestManager

  • AFHTTPSessionManager

    -(void)GET

    {
         // GET
         AFHTTPSessionManager *mgr = [AFHTTPSessionManager manager];
         // 將數據做爲參數傳入
         NSDictionary *dict = @{
                      @"username":@"12",
                      @"pwd":@"13"
                      };
         [mgr GET:[NSString stringWithFormat:@"http://123.123.123.123/login"] parameters:dict success:^(NSURLSessionDataTask *task, id responseObject) {
           NSLog(@"success:%@",responseObject);
         } failure:^(NSURLSessionDataTask *task, NSError *error) {
           NSLog(@"failure:%@",error);
         }];
       }
       -(void)POST
       {
         // POST
         AFHTTPSessionManager *mgr = [AFHTTPSessionManager manager];
         // 將數據做爲參數傳入
         NSDictionary *dict = @{
                      @"username":@"12",
                      @"pwd":@"13"
                      };
         [mgr POST:[NSString stringWithFormat:@"http://123.123.123.123/login"] parameters:dict success:^(NSURLSessionDataTask *task, id responseObject) {
           NSLog(@"success:%@",responseObject);
         } failure:^(NSURLSessionDataTask *task, NSError *error) {
           NSLog(@"failure:%@",error);
         }];
       }

    文件上傳:appendPartWithFileData:

-(void)upload
{
  // 文件上傳
  AFHTTPSessionManager *mgr = [AFHTTPSessionManager manager];
  // 將數據做爲參數傳入
  [mgr POST:@"http://123.123.123.123/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
    // formdata 爲要上傳的數據
//        [formData appendPartWithFileURL:[NSURL fileURLWithPath:@"/Users/song/Desktop/test.png"] name:@"file" error:nil];
    [formData appendPartWithFileData:[NSData dataWithContentsOfFile:@"/Users/song/Desktop/test.png"] name:@"file" fileName:@"wer.png" mimeType:@"image/png"];
  } success:^(NSURLSessionDataTask *task, id responseObject) {
    NSLog(@"success:%@",responseObject);
  } failure:^(NSURLSessionDataTask *task, NSError *error) {
    NSLog(@"failure:%@",error);
  }];
}

文件下載

下載文件須要返回一個保存路徑,還須要手動啓動resume

-(void)download
{
  // 下載
  AFHTTPSessionManager *mgr = [AFHTTPSessionManager manager];
  [[mgr downloadTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://123.123.123.123/resources/images/minion_02.png"]] progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
    // 下載文件須要返回一個保存路徑,還須要手動啓動resume
    NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
    return [NSURL fileURLWithPath:[path stringByAppendingPathComponent:response.suggestedFilename]];
  } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
    NSLog(@"%@",filePath.path);
  }] resume];
}

默認是解析json,若是想解析xml,須要指定管理器的解析器爲xml

若是解析其餘類型的文件,就將 responseSerializer 屬性設置爲 ADHTTPResonseSericlizer ,服務器返回什麼就接受什麼類型的數據。

-(void)returnType
{
  // 默認返回的數據時JSON,若是想返回XML,設置屬性responseSerializer
  // 若是想返回服務器上文件原本的類型,設置AFHTTPResponseSerializer
  AFHTTPSessionManager *mgr = [AFHTTPSessionManager manager];
  // responseSerializer 用來解析服務器返回的數據
  mgr.responseSerializer = [AFHTTPResponseSerializer serializer]; // 直接使用「服務器原本返回的數據」,不作任何解析
  // 告訴AFN,以XML形式解析服務器返回的數據
  //    mgr.responseSerializer = [AFXMLParserResponseSerializer serializer];
  // 將數據做爲參數傳入
  [mgr GET:[NSString stringWithFormat:@"http://123.123.123.123/resources/images/minion_02.png"] parameters:nil success:^(NSURLSessionDataTask *task,id response) {
    NSLog(@"success:%zd",[response length]);
  } failure:^(NSURLSessionDataTask *task, NSError *error) {
    NSLog(@"failure:%@",error);
  }];
}

手機聯網狀態

  • 手機聯網狀態:AFNetWorkReachabityManager

  • 蘋果自帶:Reachability ,經過通知監聽系統狀態
    手機聯網狀態: AFNetWorkReachabityManager

-(void)monitor
{
  // 監控網絡狀態
  AFNetworkReachabilityManager *mgr  = [AFNetworkReachabilityManager sharedManager];
  // 網絡狀態改變就會調用這個block
  [mgr setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
    NSLog(@"網絡狀態改變:%zd",status);
  }];
  // 打開監聽器
  [mgr startMonitoring];
  /*
  typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
   AFNetworkReachabilityStatusUnknown          = -1, // 未知
   AFNetworkReachabilityStatusNotReachable     = 0, // 未聯網
   AFNetworkReachabilityStatusReachableViaWWAN = 1, // 蜂窩網絡
   AFNetworkReachabilityStatusReachableViaWiFi = 2, // wifi
   };
   */
}

手機聯網狀態: Reachability

手機的狀態改變,會給系統發送通知,因此能夠添加監聽器,接收這個通知。

/**通知*/
@property (nonatomic, strong) Reachability *reach;
-(void)viewDidLoad
{
  [super viewDidLoad];
  // 添加通知
  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getNetworkStatus) name:kReachabilityChangedNotification object:nil];
  // 接收通知
  self.reach = [Reachability reachabilityForInternetConnection];
  [self.reach startNotifier];
}
-(void)getNetworkStatus
{
  /*
   typedef enum : NSInteger {
   NotReachable = 0, // 網絡不可知
   ReachableViaWiFi, // WIFI
   ReachableViaWWAN  // 移動網絡
   } NetworkStatus;
   */
  // 獲取手機網絡狀態
  if([Reachability reachabilityForLocalWiFi].currentReachabilityStatus != NotReachable)
  {
    NSLog(@"wifi");
  }
  else if([Reachability reachabilityForInternetConnection].currentReachabilityStatus != NotReachable)
  {
    NSLog(@"3G?4G");
  }
  else
  {
    NSLog(@"Nothing at all!");
  }
}
-(void)dealloc
{
  // 中止監聽器
  [self.reach startNotifier];
  // 移除通知
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kReachabilityChangedNotification object:nil];
}

MD5加密

使用:主要是對用戶的敏感信息進行加密

對輸入信息生成惟一的128位散列值(32個字符)
根據輸出值,不能獲得原始的明文,即其過程不可逆
MD5改進

加鹽(Salt):在明文的固定位置插入隨機串,而後再進行MD5
先加密,後亂序:先對明文進行MD5,而後對加密獲得的MD5串的字符進行亂序
等等,就是讓信息更加複雜

HTTPS

使用https要實現代理方法 didReceiveChallenge

1 建立HTTPS連接

_dataTask = [self.session dataTaskWithURL:[NSURL URLWithString:@"https://www.apple.com/"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }];

2 實現代理方法 didReceiveChallenge

/** 代理方法
 * challenge : 挑戰、質詢
 * completionHandler : 經過調用這個block,來告訴URLSession要不要接收這個證書
 */
-(void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
  // NSURLSessionAuthChallengeDisposition : 如何處理這個安全證書
  // NSURLCredential :安全證書
//    NSLog(@"%@",challenge);
  if (![challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
    return;
  }
  if(completionHandler)
  {
    // 利用這個block說明使用這個證書
    completionHandler(NSURLSessionAuthChallengeUseCredential,challenge.proposedCredential);
  }
}

UIWebView

顯示網頁數據
代理方法 <UIWebViewDelegate>

shouldStartLoadWithRequest : 請求以前判斷是否容許訪問(過濾某些網址)
屬性UIScrollView能夠控制滾動範圍
loadHTMLString
loadData:
能夠加載網絡資源和本地資源
scalesPageToFit 屏幕自適應
dataDetectorTypes 自動檢測網頁中出現的電話號碼,網址等,添加下劃線和連接。

// 始發送請求(加載數據)時調用這個方法
-(void)webViewDidStartLoad:(UIWebView *)webView;
// 請求完畢(加載數據完畢)時調用這個方法
-(void)webViewDidFinishLoad:(UIWebView *)webView;
// 請求錯誤時調用這個方法
-(void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;
// UIWebView在發送請求以前,都會調用這個方法,若是返回NO,表明中止加載請求,返回YES,表明容許加載請求
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;

//每當webView即將發送一個請求以前,都會調用這個方法
//返回YES:容許加載這個請求
// 返回NO:禁止加載這個請求

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
     NSLog(@"%s",__func__);
    if ([request.URL.absoluteString containsString:@"life"]) {
        return NO;
    }
    return YES;
}

JS介紹

HTML5

html(內容) + CSS(樣式) + JS(動態效果、事件交互)
經常使用JS函數

-alert(10); // 彈框 
document.getElementById(‘test’); // 根據ID得到某個DOM元素

JS和OC通訊
oc執行js

stringByEvaluatingJavaScriptFromString
JS 函數
function
JS執行OC

經過代理法方法 shouldStartLoadWithRequest
在js函數中調用 loaction.href = 'slq://sendMessage_?參數1&參數2';
傳遞參數的話,在方法後邊寫入一符號(_、@等)標識要傳遞參數,而後參數之間也要加入符號分割

OC執行JS

是用OC執行那個JS腳本

stringByEvaluatingJavaScriptFromString
   [webView stringByEvaluatingJavaScriptFromString:@"alert(100)"];
   // 調用JS中的函數,
    NSLog(@"%@",[webView stringByEvaluatingJavaScriptFromString:@"login();"]);
    self.title = [webView stringByEvaluatingJavaScriptFromString:@"document.title;"];

JS執行OC

經過代理法方法 shouldStartLoadWithRequest

無參數傳遞

//經過這個方法完成JS調用OC
 // JS和OC交互的第三方框架:WebViewJavaScriptBridge
 
 // location.href = 'slq://call';
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
  //
  NSLog(@"%@",request.URL);
  NSString *url = request.URL.absoluteString;
  NSString *pre = @"slq://";
  if([url hasPrefix:pre])
  {
    // 調用OC方法
//        NSLog(@"調用OC方法");
    NSString *method = [url substringFromIndex:pre.length];
    // NSSelectorFromString 將字符串轉換成方法名
    [self performSelector:NSSelectorFromString(method) withObject:nil];
    return NO;
  }
//    NSLog(@"發送請求");
  return YES;
}

2個參數

使用'_'代替':','?'區分參數和函數名,'&'區分參數

// location.href = 'slq://call2_number2_?&100';
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
  NSString *url = request.URL.absoluteString;
  NSString *pre = @"slq://";
  if([url hasPrefix:pre])
  {
    // 調用OC方法
    NSString *method = [url substringFromIndex:pre.length];
    // method = call2_number2_?200&300
    // 分割字符串
    NSArray *arr = [method componentsSeparatedByString:@"?"];
    // call2:number2:
    NSString *methodName = [[arr firstObject] stringByReplacingOccurrencesOfString:@"_" withString:@":"];
    // 200&300
    NSString *paramStr = [arr lastObject];
    NSArray *params = nil;
    if (arr.count == 2 && [paramStr containsString:@"&"]) {
      params = [paramStr componentsSeparatedByString:@"&"];
    }
    NSString *param1 = [params firstObject];
    NSString *param2 = params.count <= 1 ? nil : [params lastObject];
    NSLog(@"%@",methodName);
    [self performSelector:NSSelectorFromString(methodName) withObject:param1 withObject:param2]; // 兩個參數
//        [self performSelector:NSSelectorFromString(methodName) withObject:para];// 一個參數
    return NO;
  }
  return YES;
}

3個參數

若是有3個以上參數,只能使用方法簽名的方式來肯定傳遞參數

-(id)performSelector:(SEL)selector withObjects:(NSArray *)params
{
  // 設置方法簽名
  NSMethodSignature *sig = [[self class] instanceMethodSignatureForSelector:selector];
  //
  if (sig == nil) {
    NSLog(@"方法沒找到");
  }
  // NSInvocation 包裝對象利用一個NSInvocation對象包裝一次方法調用(方法調用者、方法名、方法參數、方法返回值)
  NSInvocation *invo = [NSInvocation invocationWithMethodSignature:sig];
  invo.target = self;
  invo.selector = selector;
  // 設置參數
  NSInteger paramNum = sig.numberOfArguments - 2; // 包含兩個隱含參數:self and _cmd
  // 取最小值做爲參數,
  paramNum = MIN(paramNum, params.count);
  for (int i = 0 ; i < paramNum; i ++) {
    id obj = params[i];
    if ([obj isKindOfClass:[NSNull class]]) {
      continue;
    }
    [invo setArgument:&obj atIndex:i + 2]; // 從第三個參數開始
  }
  // 設置回調
  [invo invoke];
  // 返回值
  id returnVal = 0;
  if (sig.methodReturnLength) {
    [invo getReturnValue:&returnVal];
  }
  return returnVal;
}

程序崩潰處理

在appdelegate中判斷

void handleException(NSException *exception)
{
  [[UIApplication sharedApplication].delegate performSelector:@selector(handle)];
}
-(void)handle
{
  UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"哈哈" message:@"崩潰了把" delegate:self cancelButtonTitle:@"好的" otherButtonTitles:nil, nil];
  [alertView show];
  // 從新啓動RunLoop
  [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
  [[NSRunLoop currentRunLoop] run];
}
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
  NSLog(@"-------點擊了好的");
  exit(0);
}
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  // 設置捕捉異常的回調
  NSSetUncaughtExceptionHandler(handleException);
  return YES;
}

去除Xcode編譯警告

若是對於某些警告須要屏蔽,須要找到這個警告 的代號

// 去除Xcode編譯警告
//#pragma clang diagnostic push // 開始
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
//#pragma clang diagnostic pop // 結束
異常處理

若是方法名錯誤, 拋出異常 @throw
捕獲異常 @try @catch @finally


分享來源:
http://www.tuicool.com/articl...

相關文章
相關標籤/搜索