網絡
基本概念
- 客戶端:client
- 服務器:server
- 請求:request
- 響應:response
- 過程
- 客戶端 -> 發送請求 -> 服務器(連接數據庫)
- 服務器 -> 發送響應 -> 客戶端
- 客戶端(移動端)
- 前段(前台)
- iOS,Android
- 服務器(后端)
- 后台
- Java、PHP、.NET
- 遠程服務器-面向所有用戶(上線)
- 本地服務器-面向公司內部(測試)
- URL
- 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的對應關系
JSON | OC |
---|---|
{ } | 字典 NSDictionry |
[] | 數組 NSArray |
“ ” | 字符串 NSString |
10,10.4 | NSNumber |
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播放器
- 只能播放有限的幾種格式(mp4、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 方式
- 第三方庫
- libxml2 : 純C語言,默認包含在iOS SDK 中,,同時支持SAX、DOM
- GDataXML : DOM方式,由google開發,基於libxml2
- 建議
- 大文件 : NSXMLParser、libxml2
- 小文件 : GDataXML、XSXMLParser、libxml2
XSXMLParser
- 使用過程
- 1、設置源,初始化一個XSXMLParser對象
- 2、設置代理,實現代理方法
- 3、啟動掃描
// 初始化方法
- (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/codeformat/json
- 將服務器返回的字典或者數組寫成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
*/
// 文件上傳
// 1、創建請求
NSURL *url = [NSURL URLWithString:@"http://123.123.123.123/upload"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 2、設置協議
request.HTTPMethod = @"POST";
// 3、設置請求頭
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",SLQBoundary] forHTTPHeaderField:@"Content-Type"];
// 4、設置請求體
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目錄下面,下載完畢后要自己移動文件
- 創建task(
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__);
}
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
- GET\POST
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