> 不積跬步浙宜,無以至千里;不積小流官辽,無以成江海∷谒玻——荀子
[TOC]
2017-03-06
一同仆、蘋果核 - 菜鳥不要怕,看一眼裙品,你就會用GCD俗批,帶你裝逼帶你飛
1.Serial Diapatch Queue 串行隊列
//第一個參數(shù)是隊列名字。第二個參數(shù)是隊列類型市怎,如果設(shè)置為NULL岁忘,創(chuàng)建出來的是串行隊列
dispatch_queue_t serialDispatchQueue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_SERIAL);
2.Concurrent Diapatch Queue 并發(fā)隊列
//與串行隊列剛好相反,他不會存在任務(wù)間的相互依賴区匠。
dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_CONCURRENT);
3.Global Queue & Main Queue
Global Queue其實就是系統(tǒng)創(chuàng)建的Concurrent Dispatch Queue
Main Queue 其實就是系統(tǒng)創(chuàng)建的位于主線程的Serial Dispatch Queue
通常情況我們會把這2個隊列放在一起使用干像,也是我們最常用的開異步線程-執(zhí)行異步任務(wù)-回主線程的一種方式
- Global Queue的優(yōu)先級:
DISPATCH_QUEUE_PRIORITY_HIGH 2
DISPATCH_QUEUE_PRIORITY_DEFAULT 0
DISPATCH_QUEUE_PRIORITY_LOW (-2)
DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
- 異步主線程 dispatch_async(dispatch_get_main_queue(), ^{});
在日常工作中,除了在其他線程返回主線程的時候需要用這個方法驰弄,還有一些時候我們在主線程中直接調(diào)用異步主線程蝠筑,這是利用dispatch_async的特性:block中的任務(wù)會放在主線程本次runloop之后返回。 這樣揩懒,有些存在先后順序的問題就可以得到解決了什乙。
4.dispatch_set_target_queue
dispatch_set_target_queue為自己創(chuàng)建的隊列設(shè)置優(yōu)先級
5.dispatch_after
這個是最常用的,用來延遲執(zhí)行的GCD方法已球,因為在主線程中我們不能用sleep來延遲方法的調(diào)用臣镣,所以用它是最合適的
6.dispatch_group
當(dāng)我們需要監(jiān)聽一個并發(fā)隊列中,所有任務(wù)都完成了智亮,就可以用到這個group忆某,因為并發(fā)隊列你并不知道哪一個是最后執(zhí)行的,所以以單獨一個任務(wù)是無法監(jiān)聽到這個點的阔蛉,如果把這些單任務(wù)都放到同一個group弃舒,那么,我們就能通過dispatch_group_notify方法知道什么時候這些任務(wù)全部執(zhí)行完成了。
通過把這個并發(fā)隊列任務(wù)統(tǒng)一加入group中聋呢,group每次runloop的時候都會調(diào)用一個方法dispatch_group_wait(group, DISPATCH_TIME_NOW)苗踪,用來檢查group中的任務(wù)是否已經(jīng)完成,如果已經(jīng)完成了削锰,那么會執(zhí)行dispatch_group_notify的block
7.dispatch_barrier
此方法的作用是在并發(fā)隊列中通铲,完成在它之前提交到隊列中的任務(wù)后打斷,單獨執(zhí)行其block器贩,并在執(zhí)行完成之后才能繼續(xù)執(zhí)行在他之后提交到隊列中的任務(wù)颅夺。
這其實就起到了一個線程鎖的作用,在多個線程同時操作一個對象的時候蛹稍,讀可以放在并發(fā)進行吧黄,當(dāng)寫的時候,我們就可以用dispatch_barrier_async方法唆姐,效果杠杠的稚字。
8.dispatch_sync
dispatch_sync 會在當(dāng)前線程執(zhí)行隊列,并且阻塞當(dāng)前線程中之后運行的代碼厦酬,所以胆描,同步線程非常有可能導(dǎo)致死鎖現(xiàn)象,我們這邊就舉一個死鎖的例子:
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"有沒有同步主線程?");
});
根據(jù)FIFO(先進先出)的原則仗阅,block里面的代碼應(yīng)該在主線程此次runloop后執(zhí)行昌讲,但是由于他是同步隊列,所有他之后的代碼會等待其執(zhí)行完成后才能繼續(xù)執(zhí)行减噪,2者相互等待短绸,所以就出現(xiàn)了死鎖。
9.dispatch_apply
這個方法用于無序查找筹裕,在一個數(shù)組中醋闭,我們能開啟多個線程來查找所需要的值
10.dispatch_suspend & dispatch_resume
隊列掛起和恢復(fù)
11.Semaphore
我們可以通過設(shè)置信號量的大小,來解決并發(fā)過多導(dǎo)致資源吃緊的情況朝卒,以單核CPU做并發(fā)為例证逻,一個CPU永遠只能干一件事情,那如何同時處理多個事件呢抗斤,聰明的內(nèi)核工程師讓CPU干第一件事情囚企,一定時間后停下來,存取進度瑞眼,干第二件事情以此類推龙宏,所以如果開啟非常多的線程,單核CPU會變得非常吃力伤疙,即使多核CPU银酗,核心數(shù)也是有限的,所以合理分配線程,變得至關(guān)重要黍特,那么如何發(fā)揮多核CPU的性能呢蛙讥?如果讓一個核心模擬傳很多線程,經(jīng)常干一半放下干另一件事情衅澈,那效率也會變低键菱,所以我們要合理安排谬墙,將單一任務(wù)或者一組相關(guān)任務(wù)并發(fā)至全局隊列中運算或者將多個不相關(guān)的任務(wù)或者關(guān)聯(lián)不緊密的任務(wù)并發(fā)至用戶隊列中運算今布,所以用好信號量,合理分配CPU資源拭抬,程序也能得到優(yōu)化部默,當(dāng)日常使用中,信號量也許我們只起到了一個計數(shù)的作用造虎,真的有點大材小用傅蹂。
dispatch_semaphore_t semapore = dispatch_semaphore_create(10);//為了讓一次輸出10個,初始信號量為10
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for(int i = 0; i < 20; i++){
//第二個參數(shù)為timeout 遞減信號量算凿,如果信號量小于零份蝴,會一直等待到一個信號發(fā)生
dispatch_semaphore_wait(semapore, DISPATCH_TIME_FOREVER);//每進來1次,信號量-1;進來10次后就一直hold住氓轰,直到信號量大于0婚夫;
dispatch_async(queue, ^{
NSLog(@"%i",i);
sleep(2);
//遞增信號量的值,如果上一個值小于0署鸡,該方法返回前會喚醒一個等待的線程
dispatch_semaphore_signal(semapore);//由于這里只是log,所以處理速度非嘲覆冢快,我就模擬2秒后信號量+1;
});
}
12.dispatch_once
這個函數(shù)一般是用來做一個真的單例靴庆,也是非常常用的时捌。
2017-03-07
一、 sunny分享
CFRunLoopSource
- Source是RunLoop的數(shù)據(jù)源抽象類(protocol)
- RunLoop定義了兩個Version的Source:
Source0:處理App內(nèi)部事件炉抒、App自己負責(zé)管理(觸發(fā))奢讨,如UIEvent、CFSocket
Source1:由RunLoop和內(nèi)核管理焰薄,Mach port驅(qū)動禽笑,如CFMachPort、CFMessagePort
CFRunLoopMode
RunLoop在同一段時間只能且必須在一種特定Mode下Run
更換Mode時蛤奥,需要停止當(dāng)前Loop佳镜,然后重啟新Loop
Mode是iOS App滑動順暢的關(guān)鍵
可以定制自己的Mode
NSDefaultRunLoopMode
默認(rèn)狀態(tài)、空閑狀態(tài)
UITrackingRunLoopMode
滑動ScrollView時
UIInitializationRunLoopMode
私有凡桥,App啟動時
NSRunLoopCommonModes
Mode集合
2017-03-08
一蟀伸、iOS 無需SDK,一行代碼調(diào)用主流地圖
高德地圖、百度地圖和騰訊地圖url規(guī)則:
/*
必要參數(shù)的意思:sourceApplication:該app名字啊掏; poi name:目的地名稱蠢络;lat/lon:目的地經(jīng)緯度
dev 參數(shù)進行解釋:dev支持的值為"0"和"1",即是否需要進行國測局坐標(biāo)加密迟蜜。 如果傳入的坐標(biāo)已經(jīng)是國測局坐標(biāo)則傳入0刹孔,如果傳入的是地球坐標(biāo),則該參數(shù)傳入1
*/
#define GaoDeNavUrl @"iosamap://navi?sourceApplication=%@&backScheme=%@&poiname=%@&poiid=BGVIS&lat=%@&lon=%@&dev=0&style=2"
#define GaoDeGPSUrl @"iosamap://viewMap?sourceApplication=%@&poiname=%@&lat=%@&lon=%@&dev=0"
/*
百度地圖的參數(shù)意思就比較簡單了娜睛,對mode做一個解釋髓霞,mode為調(diào)用地圖之后的導(dǎo)航方式,除了walking(步行)還有driving(駕車)和transit(公交)
origin=latlng:0,0 這個參數(shù)雖然意思上是要給一個當(dāng)前坐標(biāo)畦戒,但是可以隨意設(shè)置方库,這里設(shè)置兩個0,不影響導(dǎo)航
*/
#define BaiDuNavUrl @"baidumap://map/direction?origin=latlng:0,0|name:我的位置&destination=latlng:%@,%@|name:%@&mode=walking"
#define BaiDuGPSUrl @"baidumap://map/marker?location=%@,%@&title=我的位置&content=%@"
//騰訊地圖導(dǎo)航
#define TXGPSUrl @"qqmap://map/marker?marker=coord:%@,%@;title:%@;addr:%@"
#define TXNavUrl @"qqmap://map/routeplan?type=walk&from=我的位置&to=%@&tocoord=%@,%@&referer=%@"
2017-03-09
一障斋、UIView形變動畫
當(dāng)UIView發(fā)生變換(transformed)后纵潦,不要使用frame屬性,因為它往往不能代表視圖的正確位置垃环,使用bounds和center屬性代替邀层。見UIView(UIViewGeometry)中的說明
anchorPoint在視圖左上角為(0,0),在視圖右下角為(1,1),默認(rèn)(0.5,0.5)。蘋果文檔中注釋是錯誤的(normalized layer coordinates - '(0, 0)' is the bottom left corner of the bounds rect)遂庄。
如果更改了一個圖層的anchorPoint寥院,那么這個圖層會發(fā)生位移。iOS圍繞某點縮放或旋轉(zhuǎn)的AnchorPoint的設(shè)定
point涧团、anchorPoint和frame三者的關(guān)系:
position.x = frame.origin.x + anchorPoint.x * bounds.size.width只磷;
position.y = frame.origin.y + anchorPoint.y * bounds.size.height
這將是你最后一次糾結(jié)position與anchorPoint!
- 修改position和anchorPoint會影響frame屬性泌绣。
2017-03-10
一钮追、iOS8.0之后的隊列優(yōu)先級 MBProgressHUD庫中有用到
dispatch_queue_create函數(shù),在iOS8之前阿迈,不能在創(chuàng)建時設(shè)定優(yōu)先級元媚,所以用dispatch_set_target_queue設(shè)置優(yōu)先級,另外苗沧,如果兩個隊列dispatch_set_target_queue到同一個隊列刊棕,DISPATCH_QUEUE_SERIAL串行、DISPATCH_QUEUE_CONCURRENT并行對兩個隊列也有作用待逞,可以參考demo中方法testGCDTarget;iOS 8之后用dispatch_queue_attr_make_with_qos_class直接創(chuàng)建隊列并設(shè)置優(yōu)先級等甥角;QOS_CLASS_USER_INITIATED:為iOS8之后的優(yōu)先級,其它優(yōu)先級為:
- QOS_CLASS_USER_INTERACTIVE: user interactive等級表示任務(wù)需要被立即執(zhí)行以提供好的用戶體驗识樱。使用它來更新UI嗤无,響應(yīng)事件以及需要低延時的小工作量任務(wù)震束。這個等級的工作總量應(yīng)該保持較小規(guī)模。
- QOS_CLASS_USER_INITIATED:user initiated等級表示任務(wù)由UI發(fā)起并且可以異步執(zhí)行当犯。它應(yīng)該用在用戶需要即時的結(jié)果同時又要求可以繼續(xù)交互的任務(wù)垢村。
- QOS_CLASS_UTILITY:utility等級表示需要長時間運行的任務(wù),常常伴隨有用戶可見的進度指示器嚎卫。使用它來做計算嘉栓,I/O,網(wǎng)絡(luò)拓诸,持續(xù)的數(shù)據(jù)填充等任務(wù)侵佃。這個等級被設(shè)計成節(jié)能的。
- QOS_CLASS_BACKGROUND:background等級表示那些用戶不會察覺的任務(wù)恰响。使用它來執(zhí)行預(yù)加載趣钱,維護或是其它不需用戶交互和對時間不敏感的任務(wù)涌献。
摘自:iOS用YYLabel中方法實現(xiàn)異步畫圖ChartView
2017-03-11
一胚宦、簡易音樂播放器(仿網(wǎng)易云音樂)
繞Z軸無限旋轉(zhuǎn)動畫代碼:
rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnimation.duration = duration;
rotationAnimation.repeatCount = INFINITY;
rotationAnimation.toValue = @(M_PI * 2);
[self.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
繼續(xù)旋轉(zhuǎn)和暫停旋轉(zhuǎn)代碼:
//繼續(xù)旋轉(zhuǎn)
CFTimeInterval pausedTime = [self.layer timeOffset];
self.layer.speed = 1.0;
self.layer.timeOffset = 0.0;
self.layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
self.layer.beginTime = timeSincePause;
//暫停旋轉(zhuǎn)
CFTimeInterval currTimeoffset = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil];
self.layer.speed = 0.0;
self.layer.timeOffset = currTimeoffset;
- 暫停就是將layer的速度設(shè)置為0,將timeOffset設(shè)置為暫停時動畫進行的時間燕垃。
- 繼續(xù)動畫就是將layer的速度設(shè)置為1枢劝,獲取暫停動畫時設(shè)置的timeOffset和動畫的運行時間,計算并設(shè)置動畫的beginTime卜壕。
二您旁、CABasicAnimation使用總結(jié)
防止動畫結(jié)束后回到初始狀態(tài)
transformAnima.removedOnCompletion = NO;
transformAnima.fillMode = kCAFillModeForwards;
解釋:為什么動畫結(jié)束后返回原狀態(tài)?
首先我們需要搞明白一點的是轴捎,layer動畫運行的過程是怎樣的鹤盒?其實在我們給一個視圖添加layer動畫時,真正移動并不是我們的視圖本身侦副,而是 presentation layer 的一個緩存侦锯。動畫開始時 presentation layer開始移動,原始layer隱藏秦驯,動畫結(jié)束時尺碰,presentation layer從屏幕上移除,原始layer顯示译隘。這就解釋了為什么我們的視圖在動畫結(jié)束后又回到了原來的狀態(tài)亲桥,因為它根本就沒動過。
這個同樣也可以解釋為什么在動畫移動過程中固耘,我們?yōu)楹尾荒軐ζ溥M行任何操作题篷。
所以在我們完成layer動畫之后,最好將我們的layer屬性設(shè)置為我們最終狀態(tài)的屬性厅目,然后將presentation layer 移除掉番枚。
2017-03-12
一偿枕、UISlider使用總結(jié)
1.UISlider默認(rèn)track的高度是2,怎么修改其高度户辫?
重寫其- (CGRect)trackRectForBounds:(CGRect)bounds
方法
- (CGRect)trackRectForBounds:(CGRect)bounds
{
return CGRectMake(0, (bounds.size.height - 2) * 0.5, bounds.size.width, 2);
}
2.UISlider默認(rèn)的thumb的中心點不能滑動到UISlider的起點和終點渐夸,怎么做到像網(wǎng)易云音樂那樣可以滑動到起點和終點?
重寫其- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value
方法:
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value
{
CGRect frame = [super thumbRectForBounds:bounds trackRect:rect value:value];
CGFloat progress = value / (self.maximumValue - self.minimumValue);
CGFloat pastX = progress * self.frame.size.width;
frame.origin.x = pastX - frame.size.width * 0.5;
return frame;
}
3.如果像網(wǎng)易云音樂那樣拖拽UISlider的時候只更新時間渔欢,不播放對應(yīng)時間點的音頻墓塌。而在拖拽結(jié)束后才播放該時間點處的音頻?
綁定UISlider的UIControlEventTouchUpInside|UIControlEventTouchUpOutside事件可監(jiān)聽拖拽結(jié)束奥额。
sliderValueChanged:
方法處更新時間苫幢。sliderTouchUp:
方法處開始播放。
[_slider addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged];
[_slider addTarget:self action:@selector(sliderTouchUp:) forControlEvents:UIControlEventTouchUpInside|UIControlEventTouchUpOutside];