談?wù)凚lock(一)

? 蘋果在Mac OS X10.6 和iOS 4之后引入block語法终娃,之后就大幅改變了OC 的編程方式。Block 是Cocoa和Cocoa框架的匿名函數(shù)的實現(xiàn)券敌。所謂匿名函數(shù),就是一段具有對象性質(zhì)的代碼段,一方面這一段代碼可以當(dāng)作函數(shù)來執(zhí)行扁达,另一方面,又可以當(dāng)作對象來進行傳遞蠢熄,所以可以讓某代碼段變成某個對象的屬性跪解,或是當(dāng)作方法或者是函數(shù)的參數(shù)傳遞,也是因為這種特性签孔,我們使用block 來實現(xiàn)回調(diào)叉讥。

在Block之前,最常見的是使用代理來處理回調(diào)(或者使用較具c語言風(fēng)格的方式饥追,傳遞回調(diào)函數(shù)的指針图仓,或者使用target/action pattern)。在iOS 4有了block之后但绕,蘋果改寫了UIKit的api,把原來使用代理的地方換成了使用block救崔。

Block 語法

一直以來還是有不少人不滿block的語法惶看,甚至有人搞了個叫做http://fuckingblocksyntax.com?的網(wǎng)站,“fucking” 呵呵六孵。

將block定義成變量:

定義成property 的語法:

定義成方法的參數(shù)的語法是:


在執(zhí)行某個需要傳入block當(dāng)作參數(shù)的方法的時候 纬黎,則是用以下這種方式調(diào)用。這也是絕大多數(shù)用block當(dāng)作回調(diào)的處理方式:


把一種block定義成typedef:


Block 也可以當(dāng)成 C 函數(shù)的參數(shù)或是返回值的類型劫窒,但是本今,在這種狀況下, 我們不能夠直接使用 returnType (^)(parameterTypes) 這種語法烛亦,必須要先定義成 typedef 才行诈泼。也就是說,這樣是不合法的:

(void (^)(void)) test ( (void (^)(void)) ?block) {

return?block;

}

但可以寫成這樣:

typedef void (^TestBlock)(void);

TestBlock test(TestBlock block) {

return block;

}

雖然 C 語言的函數(shù) 的參數(shù)不能夠使用 returnType (^)(parameterTypes) 語法煤禽,但是一 個 block 倒是可以使用這種語法編寫輸入與返回值的類型铐达,但其實在這種情況下, 還是會比較建議使用 typedef 定義檬果。比方說瓮孙,我們現(xiàn)在要定義一個 block,這個 block 會返回另外一個類型為 int(^)(void) 的 block选脊,就會寫成這樣:

int (^(^counter_maker)(void))(void) = ^ {

? ? ? ? ? __block int x = 0;

? ? ? ? ? return ^ {

? ? ? ? ? ? ?return ++x;

};

};

但是這樣做 杭抠,可讀性極差,我們再來試試下面這這種吧:

typedef int (^CounterMakerBlock)(void);?

CounterMakerBlock (^counter_maker)(void) = ^ {

? ? ? ? ? ?__block int x = 0;

? ? ? ? ? ?return ^ {

? ? ? ? ? ?return ++x;

?};

?};

Block 如何代替了 Delegate

要想知道block的使用場景恳啥,不妨先看看蘋果官方是怎樣使用的偏灿。

首先是UIView 動畫。當(dāng)我們想改變一個ui 控件的frame,并加上一些動畫钝的,讓其顯得不那么生硬翁垂,我們常常會使用到UIView Animation.

栗如,我們想改變某個subView的frame:

self.subview.frame = CGRectMake(10, 10, 100, 100);

在ios4的時代硝桩,我們需要使用UIView的+beginAnimations:context: 與 +commitAnimations 兩個類方法沿猜,把原本的代碼 包起來,那么碗脊,在這兩個類方法之間的代碼就會產(chǎn)生動畫效果啼肩。

[UIView beginAnimations:@"animation" context:nil]; ? ? ? ? ? ? ?

?self.subview.frame = CGRectMake(10, 10, 100, 100); ? ? ? ? ? ? ? ? ? ? ? ? ?

?[UIView commitAnimations];

倘若我們想要在這段動畫結(jié)束的時候去做一件事情,比如執(zhí)行另一個動畫衙伶,我們應(yīng)如何呢祈坠?ios 4 之前是遵守UIView代理,實現(xiàn)其中的animationDidStop代理方法矢劲。

- (void)moveView ?{

?[UIView beginAnimations:@"animation" context:nil]; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?[UIView setAnimationDelegate:self]; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?[UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? self.subview.frame = CGRectMake(10, 10, 100, 100); ? ? ? ? ? ?[UIView commitAnimations]; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

} ? ? ? ? ? ? ? ? ? ? ? ? ? ?

- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context{ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // do something

}

由此可見? 颁虐,使用代理設(shè)計模式,最終的結(jié)果會導(dǎo)致代碼分散卧须。但是block之后另绩,我們可以將“動畫該做什么”和”動畫完成之后該做什么寫道一起了:

- (void)moveView { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?[UIView animateWithDuration:0.25 animations:^{ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? self.subview.frame = CGRectMake(10, 10, 100, 100); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?} completion:^(BOOL finished) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// Do something ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}]; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }

另外像NSArray的排序儒陨,以往我們必須使用c函數(shù)指針或者是selector的方式:

NSArray*arr = [[NSArrayalloc] initWithObjects:@1, @2, @3,nil];

SELsel =@selector(compare:);

arr = [arr sortedArrayUsingSelector:sel];

也可用block:

?NSArray*arr = [[NSArrayalloc] initWithObjects:@1, @2, @3,nil]; ?

?NSArray *arr= [array sortedArrayUsingComparator: ^NSComparisonResult(id obj1, id

?obj2) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

?return [obj1 compare:obj2]; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

}];


什么時候用 Blocks,什么時候用 Delegate笋籽?


即使block可以取代代理處理回調(diào)蹦漠,但是蘋果自己的api設(shè)計中可以看到,并不是所有的代理都被block的取代车海,在cocoa與cocoatouch中笛园,仍然大量使用代理。那么我們就要問了:究竟什么時候我們該用代理侍芝,什么時候用block呢

區(qū)分它們的方法是:如果我們調(diào)用一個方法研铆,這個方法只有一個單一的回調(diào),那么就使用block州叠,如果可能有多個不同的回調(diào)棵红,那就使用代理。

好處是:如果我們調(diào)用一個方法咧栗,這個方法只有一個單一的回調(diào)逆甜,很有可能是有一些回調(diào)是非必須實現(xiàn)的。如果是使用代理致板,那么交煞,在代理需要實現(xiàn)的protocol中,我們可以用@required與@optional區(qū)分哪些是需要實現(xiàn)的代理方法斟或;但如果用block就很難做出這樣的區(qū)分了素征,尤其是在xcode6.3之前,oc還沒有nullable,nonnull等關(guān)鍵字萝挤,去讓我們知道某個property,或者某個方法的傳入?yún)?shù)block可否為nil御毅,我們也搞不清傳入nil會發(fā)生什么可怕的事情。

舉個栗子平斩。在ios 7之后亚享,蘋果鼓勵開發(fā)者使用NSURLSession 處理網(wǎng)絡(luò)連接咽块,NSURLSession就體現(xiàn)了單一回調(diào)使用block绘面,多重回調(diào)使用代理這一點。

如果我們要從后臺獲取數(shù)據(jù)侈沪,我們只需創(chuàng)建一個NSURLSessionDataTask類的對象揭璃,一般來說,我們只需要處理「這個鏈接做完事情的 下一步該做什么」亭罪,所以一般我們只要實現(xiàn)這個task 的 completion handler瘦馍,就是鏈接完成后要執(zhí)行的block;一般鏈接結(jié)束后就是成功獲取數(shù)據(jù)或者鏈接失敗兩種情況:

NSURL *URL = [NSURL URLWithString:@"http://baidu.com"];

?NSURLRequest ?*request = [NSURLRequest requestWithURL:URL]; ? ? ? ? ? ? ? ? ? ? ? ? ??

NSURLSessionDataTask*task = [[[NSURLSession ?sharedSession]dataTaskWithRequest:request completionHandler:^(NSData* _Nullable data, NSURLResponse * _Nullable response,NSError* _Nullable error)

{

// code after completion of task

}];

[task resume];

但,NSURLSession 本身還有 delegate应役。情组。我們在發(fā)送鏈接的時候燥筷,除了處理聯(lián)線結(jié)束要做什麼之外,有時候也可能會處理中途發(fā)生的各種狀況院崇,像 是:HTTP 收到 302 轉(zhuǎn)址肆氓、遇到有問題的 SSL驗證、server 要求用?輸入賬戶密碼底瓣,這些狀況我們要不要提示使用者谢揪?或,如果這是一個傳遞大文檔捐凭、很花時間 的鏈接拨扶,我們有沒有必要上傳進度?這些狀況還是會傳遞給給NSURLSession 的 delegate茁肠,而如果我們要處理這些狀況患民,就要實現(xiàn)以下這些 代理方法。

1.當(dāng)接收到服務(wù)器響應(yīng)的時候調(diào)用

session:發(fā)送請求的session對象

dataTask:根據(jù)NSURLSession創(chuàng)建的task任務(wù)

response:服務(wù)器響應(yīng)信息(響應(yīng)頭)

completionHandler:通過該block回調(diào)官套,告訴服務(wù)器端是否接收返回的數(shù)據(jù)

-(void)URLSession:(nonnull NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTask didReceiveResponse:(nonnull NSURLResponse *)response completionHandler:(nonnullvoid(^)(NSURLSessionResponseDisposition))completionHandler;

2.當(dāng)接收到服務(wù)器返回的數(shù)據(jù)時調(diào)用 該方法可能會被調(diào)用多次

-(void)URLSession:(nonnull NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTask didReceiveData:(nonnull NSData *)data;

3.當(dāng)請求完成之后調(diào)用該方法不論是請求成功還是請求失敗都調(diào)用該方法酒奶,如果請求失敗,那么error對象有值奶赔,否則那么error對象為空

-(void)URLSession:(nonnull NSURLSession *)session task:(nonnull NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末惋嚎,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子站刑,更是在濱河造成了極大的恐慌另伍,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绞旅,死亡現(xiàn)場離奇詭異摆尝,居然都是意外死亡,警方通過查閱死者的電腦和手機因悲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門堕汞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人晃琳,你說我怎么就攤上這事讯检。” “怎么了卫旱?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵人灼,是天一觀的道長。 經(jīng)常有香客問我顾翼,道長投放,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任适贸,我火速辦了婚禮灸芳,結(jié)果婚禮上涝桅,老公的妹妹穿的比我還像新娘。我一直安慰自己烙样,他們只是感情好苹支,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著误阻,像睡著了一般债蜜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上究反,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天寻定,我揣著相機與錄音,去河邊找鬼精耐。 笑死狼速,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的卦停。 我是一名探鬼主播向胡,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼惊完!你這毒婦竟也來了僵芹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤小槐,失蹤者是張志新(化名)和其女友劉穎拇派,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凿跳,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡件豌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了控嗜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茧彤。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖疆栏,靈堂內(nèi)的尸體忽然破棺而出曾掂,到底是詐尸還是另有隱情,我是刑警寧澤承边,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布遭殉,位于F島的核電站石挂,受9級特大地震影響博助,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜痹愚,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一富岳、第九天 我趴在偏房一處隱蔽的房頂上張望蛔糯。 院中可真熱鬧,春花似錦窖式、人聲如沸蚁飒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽淮逻。三九已至,卻和暖如春阁簸,著一層夾襖步出監(jiān)牢的瞬間爬早,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工启妹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留筛严,地道東北人。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓饶米,卻偏偏與公主長得像桨啃,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子檬输,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容