1 GCD中的定時器
1.1 CFRunLoopTimerRef
- CFRunLoopTimerRef是基于時間的觸發(fā)器
- 基本上說的就是NSTimer
1.2 GCD中的定時器
GCD中的定時器不受runloop模式影響
- (void)gcdTimer {
//1 創(chuàng)建GCD的定時器
/*
參數(shù)1:source類型 DISPATCH_SOURCE_TYPE_TIMER 定時器
參數(shù)2:描述信息 如線程ID
參數(shù)3:更詳細(xì)的描述信息
參數(shù)4:隊(duì)列,決定GCD的定時器在哪個線程中執(zhí)行
*/
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
//2 設(shè)置定時器(起始時間|間隔時間|精準(zhǔn)度)
/*
參數(shù)1:定時器
參數(shù)2:起始時間 DISPATCH_TIME_NOW 從現(xiàn)在開始計時
參數(shù)3:間隔時間 GCD中時間的單位是納秒
參數(shù)4:精準(zhǔn)度,絕對精準(zhǔn)
*/
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
//3 設(shè)置定時器執(zhí)行的任務(wù)
dispatch_source_set_event_handler(timer, ^{
NSLog(@"%@",[NSThread currentThread]);
});
//4 啟動定時器
dispatch_resume(timer);
self.timer = timer;
}
2 CFRunLoopSourceRef簡單介紹
2.1 CFRunLoopSourceRef是事件源(輸入源)
以前的分法
1)Port-Based Sources
2)Custom Input Source
3)Cocoa Perform Selector Source現(xiàn)在的分法
1)Source0:非基于Port的
2)Source1:基于Port的

3 CFRunLoopObserverRef簡單介紹
3.1 CFRunLoopObserverRef
- CFRunLoopObserverRef是觀察者,能夠監(jiān)聽RunLoop的狀態(tài)改變
- 可以監(jiān)聽的時間點(diǎn)有以下幾個
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即將進(jìn)入RunLoop
kCFRunLoopBeforeTimers = (1UL << 1), // 即將處理Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即將處理Source
kCFRunLoopBeforeWaiting = (1UL << 5), // 即將進(jìn)入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 剛從休眠中喚醒
kCFRunLoopExit = (1UL << 7), // 即將退出RunLoop
kCFRunLoopAllActivities = 0x0FFFFFFFU // 所有狀態(tài)
};
3.2 監(jiān)聽RunLoop示例
- (void)observer {
// 創(chuàng)建監(jiān)聽者
/*
參數(shù)1:怎么分配存儲空間
參數(shù)2:要監(jiān)聽的狀態(tài) kCFRunLoopAllActivities 所有狀態(tài)
參數(shù)3:是否持續(xù)監(jiān)聽
參數(shù)4:優(yōu)先級,總是傳0
參數(shù)5:當(dāng)前狀態(tài)改變時回調(diào)
*/
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
/*
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即將進(jìn)入RunLoop
kCFRunLoopBeforeTimers = (1UL << 1), // 即將處理Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即將處理Source
kCFRunLoopBeforeWaiting = (1UL << 5), // 即將進(jìn)入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 剛從休眠中喚醒
kCFRunLoopExit = (1UL << 7), // 即將退出RunLoop
kCFRunLoopAllActivities = 0x0FFFFFFFU // 所有狀態(tài)
};
*/
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"即將進(jìn)入RunLoop");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"即將處理Timer事件");
break;
case kCFRunLoopBeforeSources:
NSLog(@"即將處理Source事件");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"即將進(jìn)入休眠");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"剛從休眠中喚醒");
break;
case kCFRunLoopExit:
NSLog(@"即將退出RunLoop");
break;
default:
break;
}
});
/*
參數(shù)1:要監(jiān)聽那個runloop
參數(shù)2:觀察者
參數(shù)3:runloop運(yùn)行模式
*/
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
}
4 CFRunLoop運(yùn)行流程
4.1 RunLoop處理邏輯



5 RunLoop應(yīng)用
5.1 常見應(yīng)用
- NSTimer
- ImageView顯示
- PerformSelector
- 常駐線程
- 自動釋放池
5.2 常駐線程demo
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(task1) object:nil];
[self.thread start];
}
- (IBAction)btnClicked:(id)sender {
[self performSelector:@selector(task2) onThread:self.thread withObject:nil waitUntilDone:YES];
}
- (void)task1 {
NSLog(@"task1---%@",[NSThread currentThread]);
// 讓線程常駐不退出的解決方案:開runloop
//1 獲取子線程的runloop
NSRunLoop *currentLoop = [NSRunLoop currentRunLoop];
// 保證runloop不退出: 添加timer或者source
//NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
//[currentLoop addTimer:timer forMode:NSDefaultRunLoopMode];
[currentLoop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
// 默認(rèn)是沒有開啟的,需要開啟
[currentLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
}
- (void)task2 {
NSLog(@"task2---%@",[NSThread currentThread]);
}
- (void)run {
NSLog(@"%s",__func__);
}
5.3 自動釋放池
- 第一次創(chuàng)建:在啟動runloop的時候
- 最后一次銷毀:runloop退出的時候
- 其他時候的創(chuàng)建和銷毀:當(dāng)runloop即將睡眠的時候銷毀之前創(chuàng)建的釋放池,重新創(chuàng)建一個新的。
6 網(wǎng)絡(luò)基本概念
6.1 幾個基本概念
- 客戶端:移動應(yīng)用
- 服務(wù)端:為客戶端提供服務(wù)、提供數(shù)據(jù)、提供資源的機(jī)器
- 請求:客戶端向服務(wù)器索取數(shù)據(jù)的一種行為
- 響應(yīng):服務(wù)器對客戶端請求作出的響應(yīng),一般指返回數(shù)據(jù)給客戶端
6.2 服務(wù)器
按照軟件開發(fā)來分,服務(wù)器可以大致分為2種:
- 遠(yuǎn)程服務(wù)器
- 本地服務(wù)器
7 HTTP協(xié)議簡單介紹
7.1 URL
- 全稱是Uniform Resource Locator(統(tǒng)一資源定位符)
- 通過1個URL,能找到互聯(lián)網(wǎng)上唯一的1一個資源
- URL就是資源的地址、位置,互聯(lián)網(wǎng)上的每個資源都有一個唯一的URL
- URL的基本格式 = 協(xié)議://主機(jī)地址/路徑
1)協(xié)議:不同的協(xié)議,達(dá)標(biāo)不同的資源查找方式,資源傳輸方式
2)主機(jī)地址:存放資源的主機(jī)(服務(wù)器)的IP地址(域名)
3)路徑:資源在主機(jī)(服務(wù)器)中的具體位置
7.2 URL中的常見協(xié)議
- HTTP
超文本傳輸協(xié)議,訪問的是遠(yuǎn)程的網(wǎng)絡(luò)資源,格式是http://
http協(xié)議是在網(wǎng)絡(luò)開發(fā)中最常見的協(xié)議 - file
訪問的是本地計算機(jī)的資源,格式是file:// (不用加主機(jī)地址) - mailto
訪問的是電子郵件地址,格式是mailto: - FTP
訪問的是共享主機(jī)的文件資源,格式是ftp://
7.3 HTTP協(xié)議特點(diǎn)
- 簡單快速
- 因?yàn)镠TTP協(xié)議簡單,所以HTTP服務(wù)器的程序規(guī)模小,因而通信速度很快
- 靈活
HTTP允許傳輸各種各樣的數(shù)據(jù) - HTTP0.9和1.0使用非持續(xù)連續(xù)
限制每次連接只處理一個請求,服務(wù)器對客戶端的請求作出響應(yīng),馬上斷開連接,這種方式可以節(jié)省傳輸時間
8 GET請求和POST請求
8.1 發(fā)送HTTP請求的方法
在HTTP/1.1協(xié)議中,定義了8種http請求的方法
GET、POST、OPTIONS、HEAD、PUT、DELETE、TARCE、CONNECT、PATCH
- 根據(jù)HTTP協(xié)議的設(shè)計初衷,不同的方法對資源有不同操作方式
1)PUT:增
2)DELETE:刪
3)POST:改
4)GET:查
最常用的是GET和POST(實(shí)際上GET和POST都能辦到增刪改查)
8.2 GET和POST的對比
GET
在請求URL后面以?的形式跟上發(fā)給服務(wù)器的參數(shù),多個參數(shù)之間用&隔開,比如 http://ww.test.com/login?username=123&pwd=234&type=JSON
由于瀏覽器和服務(wù)器對URL長度有限制,因此在URL后面附帶的參數(shù)是有限制的,通常不能超過1KBPOST
發(fā)給服務(wù)器的參數(shù)全部放在請求體中
理論上,POST傳遞的數(shù)據(jù)量沒有限制(具體還得看服務(wù)器的處理能力)如何選擇【除簡單數(shù)據(jù)查詢外,其它的一律使用POST請求】
a.如果要傳遞大量數(shù)據(jù),比如文件上傳,只能用POST請求
b.GET的安全性比POST要差些,如果包含機(jī)密\敏感信息,建議用POST
c.如果僅僅是索取數(shù)據(jù)(數(shù)據(jù)查詢),建議使用GET
d.如果是增加、修改、刪除數(shù)據(jù),建議使用POST
9 HTTP通信過程
9.1 請求
HTTP協(xié)議規(guī)定:1個完整的有客戶端發(fā)送給服務(wù)器的HTTP請求包含以下內(nèi)容
- 請求頭:包含了對客戶端的環(huán)境描述、客戶端請求信息等
- 請求體:客戶端發(fā)送給服務(wù)器的具體數(shù)據(jù),比如文件數(shù)據(jù)(POST請求才會有)
9.2 響應(yīng)
客戶端向服務(wù)器發(fā)送請求,服務(wù)器應(yīng)當(dāng)作出響應(yīng),即返回數(shù)據(jù)給客戶端
- HTTP協(xié)議規(guī)定:1個完整的HTTP響應(yīng)包含以下內(nèi)容:...
- 響應(yīng)體:服務(wù)器返回給客戶端的具體數(shù)據(jù),比如文件數(shù)據(jù)
9.3 常見響應(yīng)狀態(tài)
| 狀態(tài)碼 | 英文名稱 | 中文描述 |
|---|---|---|
| 200 | ok | 請求成功 |
| 400 | Bad Request | 客戶端請求的語法錯誤,服務(wù)器無法解析 |
| 404 | Not found | 服務(wù)器無法根據(jù)客戶端的請求找到資源 |
| 500 | Internal Server Error | 服務(wù)器內(nèi)部錯誤,無法完成請求 |
9.4 iOS中發(fā)送HTTP請求的方案
在iOS中,常見的發(fā)送HTTP請求的方案有
蘋果原生(自帶)
1)NSURLConnection:用法簡單,最古老最經(jīng)典最直接的一種方案【坑比較多】
2)NSURLSession:功能比NSURLConnection更加強(qiáng)大,蘋果目前比較推薦使用這種技術(shù)【2013年推出,iOS7開始出的技術(shù)】
3)CFNetwork:NSRUL的底層,純c語言第三方框架
1)ASIHttpRequest:外號“HTTP終結(jié)者”,功能強(qiáng)大,但早已停止更新
2)AFNetworking:簡單易用,提供了基本夠用的常用功能,維護(hù)和使用者較多
3)MKNetworkKit:簡單易用,產(chǎn)自三哥的故鄉(xiāng)印度,維護(hù)和使用者較少
10 NSURLConnection
10.1 作用
- 負(fù)責(zé)發(fā)送請求,建立客戶端和服務(wù)器的鏈接
- 發(fā)送數(shù)據(jù)給服務(wù)器,并收集來自服務(wù)器的響應(yīng)數(shù)據(jù)
10.2 使用步驟
- 創(chuàng)建一個NSURL對象,設(shè)置請求路徑
- 傳入NSURL,創(chuàng)建一個NSURLRequest對象,設(shè)置請求頭和請求體
- 使用NSURLConnection發(fā)送請求
10.3 發(fā)送同步和異步GET請求
默認(rèn)為GET請求
// 同步請求
- (void)sync {
//1 url地址
//
NSURL *url = [NSURL URLWithString:@"https://static.guxiansheng.cn/hq_info.json"];
//2 請求請求對象
// 請求頭不需要設(shè)置(默認(rèn)請求頭)
NSURLRequest *requet = [NSURLRequest requestWithURL:url];
//3 發(fā)送同步請求
/*
參數(shù)1:請求對象
參數(shù)2:響應(yīng)頭
參數(shù)3:錯誤信息
參數(shù)4:響應(yīng)體
*/
NSHTTPURLResponse *res = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:requet returningResponse:&res error:nil];
//4 解析數(shù)據(jù)
NSString *strData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",strData);
NSLog(@"%@",res);
}
// 異步請求,block方式
- (void)async1 {
//1 url地址
NSURL *url = [NSURL URLWithString:@"http://gss0.baidu.com/9fo3dSag_xI4khGko9WTAnF6hhy/zhidao/pic/item/b3b7d0a20cf431ad39010c0d4d36acaf2edd9837.jpg"];
//2 請求請求對象
// 請求頭不需要設(shè)置(默認(rèn)請求頭)
NSURLRequest *requet = [NSURLRequest requestWithURL:url];
//3 發(fā)送異步請求
/*
參數(shù)1:請求對象
參數(shù)2:隊(duì)列,決定代碼塊的調(diào)用線程
參數(shù)3:completionHandler 請求完成的時候調(diào)用
response:響應(yīng)頭
data:響應(yīng)體
connectionError:錯誤信息
*/
[NSURLConnection sendAsynchronousRequest:requet queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
//4 解析數(shù)據(jù)
NSString *strData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",strData);
NSURLResponse *res = (NSURLResponse *)response;
NSLog(@"%@",res);
}];
}
//異步請求:代理方式
- (void)async2 {
//1 url地址
NSURL *url = [NSURL URLWithString:@"https://static.guxiansheng.cn/hq_info.json"];
//2 請求請求對象
// 請求頭不需要設(shè)置(默認(rèn)請求頭)
NSURLRequest *requet = [NSURLRequest requestWithURL:url];
//3 發(fā)送異步請求
// 方式1
[NSURLConnection connectionWithRequest:requet delegate:self];
// 方式2
//[[NSURLConnection alloc] initWithRequest:requet delegate:self];
// 方式3
//NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:requet delegate:self startImmediately:YES];
//[connection start];
// 取消請求操作
//[connection cancel];
}
#pragma mark - NSURLConnectionDataDelegate
// 1 客戶端收到服務(wù)端響應(yīng)
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(@"%s",__func__);
}
//2 客戶端接收到服務(wù)端數(shù)據(jù)
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.recieveData appendData:data];
}
//3 客戶端數(shù)據(jù)請求失敗
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(@"%s",__func__);
}
//4 客戶度數(shù)據(jù)請求完畢
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
//4 解析數(shù)據(jù)
NSString *strData = [[NSString alloc] initWithData:self.recieveData encoding:NSUTF8StringEncoding];
NSLog(@"%@",strData);
}
#pragma mark - getter
- (NSMutableData *)recieveData {
if (!_recieveData) {
_recieveData = [NSMutableData data];
}
return _recieveData;
}
10.4 NSURLConnection發(fā)送POST請求
- (void)post {
//1 url地址
NSURL *url = [NSURL URLWithString:@"https://service.agent.guxiansheng.cn?c=adplace&a=getinfoad&v=App&site=marketing"];
//2 請求請求對象
NSMutableURLRequest *requet = [NSMutableURLRequest requestWithURL:url];
// 設(shè)置請求方法 POST必須大寫
requet.HTTPMethod = @"POST";
// 設(shè)置請求頭信息
[requet setValue:@"ios10.2" forHTTPHeaderField:@"User-Agent"];
// 設(shè)置超時時間
requet.timeoutInterval = 10;
//3 設(shè)置請求體
NSString *requtstBody = @"id=1&appcode=5c6bb51a113c8szji5nb6cur";
requet.HTTPBody = [requtstBody dataUsingEncoding:NSUTF8StringEncoding];
//4 發(fā)送請求
[NSURLConnection sendAsynchronousRequest:requet queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
//4 解析數(shù)據(jù)
NSString *strData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",strData);
NSURLResponse *res = (NSURLResponse *)response;
NSLog(@"%@",res);
}];
}
11 URL中文轉(zhuǎn)碼
是否需要轉(zhuǎn)碼主要看url里面是否存在中文,請求體里面的中文是不需要做轉(zhuǎn)碼處理的。
- (void)demo {
NSString *strUrl = @"https://static.guxiansheng.cn/hq_info.json?v=哈哈";
NSLog(@"%@",strUrl);
// 中文轉(zhuǎn)碼
//strUrl = [strUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
strUrl = [strUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
NSLog(@"%@",strUrl);
NSURL *url = [NSURL URLWithString:strUrl];
NSLog(@"%@",url);
}
12 JSON解析簡單介紹
12.1 什么是JSON
- JSON是一種輕量級的數(shù)據(jù)格式,一般用于數(shù)據(jù)交互
- 服務(wù)器傳給客戶端的數(shù)據(jù),一般都是
JSON格式或者XML格式(文件下載除外) - JSON的格式很像OC中的字典和數(shù)組
{"name" : "jack", "age" : 10}
{"name" : ["jack" ,"rose", "jie"]} - 標(biāo)磚JSON格式的注意點(diǎn):
key必須用雙引號 - 要想從JSON中挖掘出具體數(shù)據(jù),得對JSON進(jìn)行解析
12.1 JSON - OC 轉(zhuǎn)換對照表
| JSON | OC |
|---|---|
| 大括號{} | NSDictionary |
| 中括號[] | NSArray |
| 雙引號"" | NSString |
| 數(shù)字10、10.8 | NSNumber |
12.2 JSON解析方案
- 在iOS中,JSON的常見解析方案有4種
- 第三方框架:JSONkit、SBJson、TouchJson(性能從左到右,越差)
- 蘋果原生(自帶):NSJSONSerialization(性能最好)
// JSON -> OC 反序列化
+ (nullable id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error;
// OC -> JSON 序列化
+ (nullable NSData *)dataWithJSONObject:(id)obj options:(NSJSONWritingOptions)opt error:(NSError **)error;