iOS开发 - OC - block的详解 - 基础篇
?
深入理解oc中的block
蘋果在Mac OS X10.6 和iOS 4之后引入了block語法。這一舉動(dòng)對(duì)于許多OC使用者的編碼風(fēng)格改變很大。就我本人而言,感覺block用起來還是很爽的,但一直以來,都是知其然,而不知所以然。這篇文章一共有兩篇,其中基礎(chǔ)篇講解了block的基本的使用和創(chuàng)建,以及一些注意事項(xiàng)。在深入篇中,我將會(huì)對(duì)block的一些原理陳述出來,探討block的內(nèi)部。
基礎(chǔ)篇
1.block是什么
?首先,對(duì)于Block,我在蘋果的文檔中找到以下描述:
Block objects?are a C-level language construct that you can incorporate into your C and Objective-C code. A block object is essentially an anonymous function and the data that goes with that function, something which in other languages is sometimes called a?closure?or?lambda. Blocks are particularly useful as callbacks or in places where you need a way of easily combining both the code to be executed and the associated data.?
?block對(duì)象是一個(gè)C語言結(jié)構(gòu)體,可以并入到C和Objective-C代碼中。Block對(duì)象本質(zhì)上是一個(gè)匿名函數(shù),以及與該函數(shù)一起使用的數(shù)據(jù),其他語言中有時(shí)稱為閉包或lambda。 Block特別適用于回調(diào),或者是在你為了讓代碼看起來具有更清晰的邏輯進(jìn)行代碼的組合時(shí)使用。
上面的解釋,告訴我們: 首先,block是一個(gè)OC中的對(duì)象;并且,這個(gè)對(duì)象時(shí)一個(gè)C語言的結(jié)構(gòu)體,它可以使用在C語言和OC中;同時(shí),block本質(zhì)上是一個(gè)匿名函數(shù)和其包含的數(shù)據(jù)集中。這應(yīng)該就是block的定義了。之前經(jīng)常在一些博客上看到,將block描述成一個(gè)結(jié)構(gòu)體,或者是一個(gè)匿名函數(shù),抑或是直接說成一個(gè)對(duì)象。這些描述都有道理,但是并不全面。(在第二部分,我會(huì)詳細(xì)說明為什么)
2.為什么要使用blobk
蘋果的文檔中還描述:
In iOS, blocks are commonly used in the following scenarios:
-
-
As a replacement for delegates and delegate methods
-
As a replacement for callback functions
-
To implement completion handlers for one-time operations
-
To facilitate performing a task on all the items in a collection
-
Together with dispatch queues, to perform asynchronous tasks
-
在iOS中,在以下情況下通常使用block:
-
- 代替代理和委托方法
- 作為回調(diào)函數(shù)的替代
- 一次性編寫多次處理的任務(wù)
- 方便對(duì)集合中的所有項(xiàng)執(zhí)行任務(wù)
- 與GCD隊(duì)列一起執(zhí)行異步任務(wù)
這里告訴我們?cè)谝陨锨闆r下,我們都能夠使用block。我感受最深刻的是使用block進(jìn)行回調(diào)。很多情況下,我們可能只需要對(duì)某個(gè)事件進(jìn)行一個(gè)單一的回調(diào),也許僅僅就一次,如果我使用代理的話,我需要?jiǎng)?chuàng)建類、編寫協(xié)議,僅僅對(duì)于一個(gè)小地方的回調(diào)成本很高,那么block登場(chǎng)就恰到好處了。 除此之外,block的特性可以讓代理集中在某處,我們只需要在一個(gè)地方就可以完成回調(diào)之前和回調(diào)時(shí)的代碼,相比,使用回調(diào)函數(shù)和代理都沒有這個(gè)優(yōu)勢(shì)!另外,我們可以想到,OC中封裝好了一些集合的方法,比如,數(shù)組的排序,仔細(xì)會(huì)發(fā)現(xiàn),這里就使用block進(jìn)行回到操作的。
3.block怎么使用
首先我們看看怎么創(chuàng)建一個(gè)block吧。
?
//這樣創(chuàng)建 相當(dāng)平常的block 有參數(shù),有返回值//申明一個(gè)block 名字叫oneFrom ,右邊的float類型是參數(shù)類型,左邊的float是返回值的類型。 當(dāng)這兩者中的任意一個(gè)為空的時(shí)候都可使用void代替float (^oneFrom)(float);oneFrom = ^(float aFloat) {float result = aFloat - 1.0;NSLog(@"調(diào)用時(shí)");return result;};如果我們需要?jiǎng)?chuàng)建一個(gè)全局block(進(jìn)階篇將會(huì)細(xì)說,全局block和其他的block的區(qū)別),應(yīng)該像這樣 :
#import <stdio.h>int GlobalInt = 0; int (^getGlobalInt)(void) = ^{ return GlobalInt; }; //這種方式將申明和實(shí)現(xiàn)一起寫當(dāng)我們調(diào)用的時(shí)候,我們應(yīng)當(dāng)這樣做: ? ??
//這樣創(chuàng)建 相當(dāng)平常的block 有參數(shù),有返回值//申明一個(gè)block 名字叫oneFrom ,右邊的float類型是參數(shù)類型,左邊的float是返回值的類型。 當(dāng)這兩者中的任意一個(gè)為空的時(shí)候都可使用void代替float (^oneFrom)(float);oneFrom = ^(float aFloat) {float result = aFloat - 1.0;NSLog(@"調(diào)用時(shí)");return result;};//調(diào)用float par = 5.0;NSLog(@"調(diào)用之前源對(duì)象:%f",par);float result = oneFrom(par);NSLog(@"調(diào)用的處理結(jié)果:%f",result);NSLog(@"調(diào)用之后源對(duì)象:%f",par);運(yùn)行結(jié)果:
? ? ? 為了更加清楚的體現(xiàn)block的回調(diào)效果,我特意進(jìn)行了打印。可以發(fā)現(xiàn),盡管block的代碼早就聲明了,但是并沒有立刻調(diào)用,而是在block調(diào)用的時(shí)候才被執(zhí)行。 相信通過這里,對(duì)于block的回調(diào)應(yīng)該有一定的理解了。
? ? ? 也許,這樣用起來,不覺得太簡(jiǎn)單了嘛?根本就沒什么卵用啊! 別急,再看看另外兩種block的使用。之前說了,block時(shí)OC中的一個(gè)對(duì)象,既然是對(duì)象,我們就可以把它當(dāng)作一個(gè)類的屬性咯,應(yīng)該是也可以很其他屬性一樣,被當(dāng)作一個(gè)方法的參數(shù)吧。沒錯(cuò),是這樣的,相信block之所以被大家認(rèn)可也就在于這里吧。 ?看看這個(gè)例子。
?
假設(shè) 我有一個(gè)這樣的類:包含一個(gè)block屬性,testBlock。包含一個(gè)調(diào)用自己的block 屬性的方法- blockDo.
我在另一個(gè)地方實(shí)現(xiàn)這樣的代碼
#import "ViewController.h"@interface Computer ()@property (nonatomic,copy) NSString* (^testBlock)(NSString*) ; //將一個(gè)block作為 computer 的屬性- (void)blockDo;@end@implementation Computer- (void)blockDo{NSString *testStr = @"testData_Old";if(self.testBlock){NSLog(@"%@",self.testBlock(testStr)); //調(diào)用并打印 } }@end
在另一個(gè)地方寫下這些代碼:
Computer* computer = [[Computer alloc]init];computer.testBlock = ^(NSString *parStr){NSLog(@"%@",parStr);parStr = @"testData_New";return parStr;}; [computer blockDo]; //執(zhí)行block運(yùn)行結(jié)果:
?
? ? ?仔細(xì)看看,這里的調(diào)用就比之前復(fù)雜了。 因?yàn)槲医o它添加了一個(gè)方法,并且將block自身的調(diào)用交給了Computer,我只是實(shí)現(xiàn)了block而已,最后啟動(dòng)調(diào)用他的方法。 是的,我在另一個(gè)地方對(duì)Computer類模擬了一個(gè)方法,這個(gè)方法沒有在Computer類中實(shí)現(xiàn),我甚至可以在任何地方去實(shí)現(xiàn)它,而最后我又可以在其他的地方調(diào)用,但是它確實(shí)具備了一些功能。 這就是block的神奇的地方。 或許這樣你還是覺得它不過如此。
再來看一個(gè):
#import "ViewController.h"@interface Computer ()- (void)doSomethingFeedBack:(NSString*(^)(NSString*))handle;@end@implementation Computer- (void)doSomethingFeedBack:(NSString*(^)(NSString*))handle{NSString *handleStr = @"Old";sleep(3.0);NSLog(@"%@",handle(handleStr)); } @end其他地方調(diào)用:
[computer doSomethingFeedBack:^NSString *(NSString *parStr) {NSLog(@"%@",parStr);NSString *returnStr = [[NSString alloc]initWithFormat:@"add %@",parStr];return returnStr;}];打印結(jié)果:
這里將block作為一個(gè)參數(shù)放在?doSomethingFeedBack 函數(shù)面。體現(xiàn)了block 的對(duì)象性質(zhì),相比之下,代碼量很是簡(jiǎn)潔。這種實(shí)現(xiàn)回調(diào)的方式的邏輯更加清晰,明朗。
上面三個(gè)例子,展示block的三種不同的使用方式。它們分別是:
-
- 將block定義成變量
- 將block定義成屬性
- 將block作為參數(shù)
4.block的使用注意點(diǎn) ? ?__block和__week
? ? ?block使用的過程中,并沒有需要特別注意的地方,只需要注意兩個(gè)關(guān)鍵詞。__block和__weak修飾詞。
? ? ?我在講解創(chuàng)建第一種block的時(shí)候,運(yùn)行程序打印的結(jié)果看到一個(gè)現(xiàn)象,棧block(后續(xù)會(huì)解釋什么是棧block,這里主要區(qū)分全局bolck)雖然拿到了外部變量,但是對(duì)與變量的修改確實(shí)沒有效果的———前后的源數(shù)據(jù)的打印值沒有發(fā)生改變。如果我們需要在block中對(duì)外部的變量進(jìn)行修改,應(yīng)該在這個(gè)變量之前加上__block修飾,原因在下一品文章中詳解。
? ? ??__weak關(guān)鍵詞用于解決使用block導(dǎo)致強(qiáng)引用循環(huán)的問題。block在使用的過程中,當(dāng)block屬于某個(gè)對(duì)象,如果在block函數(shù)中,又包含了這個(gè)對(duì)象,或者包含其屬性,都會(huì)因?yàn)閎lock持有對(duì)象,對(duì)象又持有block導(dǎo)致對(duì)象得不到釋放的情況。 這是只需要在申請(qǐng)一個(gè)用 weak修飾的對(duì)象替代源對(duì)象,將引用循環(huán)打斷,保證正常釋放,具體的內(nèi)部原因在下篇中將會(huì)詳細(xì)介紹。
5.總結(jié):
? ? ?通過上面演示block的用法發(fā)現(xiàn),block每次的回調(diào)是通過它的匿名函數(shù)來進(jìn)行的,也就是每一次最多執(zhí)行一個(gè)回調(diào),在需要進(jìn)行大批量的回調(diào)的時(shí)候,就需要寫很多不同的block回調(diào),這樣的方式顯然是不合適的,相比之下,這是時(shí)候使用協(xié)議和代理的方式就自然多了。除此之外,block還比較適合用在線程之間的切換回調(diào)。GCD就是采用了多線程結(jié)合block來做的。在多數(shù)情況下,應(yīng)當(dāng)充分考慮block的可以攜帶環(huán)境的優(yōu)點(diǎn)使代碼的邏輯更加的清晰。
基礎(chǔ)篇簡(jiǎn)單的介紹下block的使用。并沒有深入研究其內(nèi)部細(xì)節(jié),在接下來的深入篇中,我將介紹:
- 為什么說block是一個(gè)結(jié)構(gòu)體,也是一個(gè)對(duì)象,同時(shí)還是攜帶數(shù)據(jù)的匿名函數(shù)
- 全局block ,棧block以及堆區(qū)block的區(qū)別和他們之間的聯(lián)系,探究block的內(nèi)存管理
- 為什么使用__block 就可以使得block可以修改外部變量
- 引起強(qiáng)引用循環(huán)的原因是什么,我們解決它的方法和原理又是什么
2017-04-01 ??18:54:21
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/FBiOSBlog/p/6667371.html
總結(jié)
以上是生活随笔為你收集整理的iOS开发 - OC - block的详解 - 基础篇的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: List的子类
- 下一篇: ROS学习之包的概念