Working with Blocks

簡(jiǎn)介

Blocks是C語(yǔ)言層級(jí)語(yǔ)法和運(yùn)行時(shí)特性腐晾。 它們類似于標(biāo)準(zhǔn)C函數(shù)叉弦,但是除了可執(zhí)行代碼之外,它們還可以保存堆棧變量藻糖。 因此淹冰,塊可以保存數(shù)據(jù),在代碼執(zhí)行時(shí)使用巨柒。
1樱拴、Block可以作為函數(shù)數(shù)調(diào)用、作為函數(shù)參數(shù)洋满、作為方法參數(shù)晶乔。
2、因?yàn)楠?dú)立完整可以在多線程中使用牺勾;
3正罢、因?yàn)閾碛谢卣{(diào)時(shí)需要執(zhí)行的代碼和執(zhí)行代碼時(shí)需要的數(shù)據(jù),常常被用來(lái)實(shí)現(xiàn)回調(diào)Callback驻民。
由于Objective-C和C++都是從C派生腺怯,因此三種語(yǔ)言均可以使用(Objective-C使用更多)。iOS從4.0版本開始支持川无。在其他語(yǔ)言環(huán)境中有時(shí)被稱為“閉包”呛占。

特點(diǎn)

Block是一個(gè)匿名的內(nèi)聯(lián)代碼集合,有以下特點(diǎn):
1懦趋、像函數(shù)一樣用參數(shù)列表
2晾虑、有可推斷或者聲明的返回值類型
3、可以從定義它作用域捕獲數(shù)據(jù)
4、可選的可以修改捕獲到的數(shù)據(jù)
5帜篇、同一作用域中的其它block共享捕獲的數(shù)據(jù)
6糙捺、作用域的堆棧被銷毀后,仍然可以繼續(xù)共享定義其范圍定義的數(shù)據(jù)
由于compiler 和 runtime 保證block和其相關(guān)的數(shù)據(jù)的生命周期笙隙,因此可以copy一份傳遞到其他地方使用洪灯。

聲明和創(chuàng)建

聲明

首先說(shuō)明下函數(shù)指針的聲明格式

返回值類型 ( * 指針變量名) ([形參列表]);

Block variables保存著block的引用【固担可以像聲明函數(shù)指針一樣聲明block變量签钩,但是要把*換成^,例如以下均為有效的聲明

void (^blockReturningVoidWithVoidArgument)(void);
int (^blockReturningIntWithIntAndCharArguments)(int, char);
void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);

Blocks支持可變的參數(shù)列表,當(dāng)沒有參數(shù)時(shí)必須寫void坏快。
通過為編譯器提供block使用的數(shù)據(jù)铅檩,傳遞參數(shù),返回值莽鸿,block設(shè)計(jì)完全類型安全昧旨。可以把block引用強(qiáng)制轉(zhuǎn)換成任意類型的指針祥得,反之亦然兔沃。但是不能使用操作符*來(lái)訪問值,因?yàn)閎lock的值在編譯期無(wú)法計(jì)算级及。
理解:假如每次方法調(diào)用當(dāng)做一次消息發(fā)送(一般底層會(huì)有很多次)粘拾,把block中的所以消息發(fā)送按照順序放到一種能夠先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)--比方說(shuō)實(shí)現(xiàn)棧特性的結(jié)構(gòu)體;那么block變量^{ ... }作用一樣都是指向結(jié)構(gòu)體的首地址创千;傳遞首地址跟其他對(duì)象指針缰雇、函數(shù)指針用法很像。

類型定義簡(jiǎn)化用法

如果經(jīng)常重復(fù)使用同一個(gè)類型的block追驴,你可以定義自己的block類型械哟,例如

//返回值類型:float 兩個(gè)參數(shù)類型:float 變量名稱:MyBlockType
typedef float (^MyBlockType)(float, float);
 
MyBlockType myFirstBlock = // ... ;
MyBlockType mySecondBlock = // ... ;

創(chuàng)建

^表示block的開始,接一個(gè)返回值類型(可選殿雪,默認(rèn)不寫)再接一個(gè)(參數(shù)列表)暇咆,后邊接一個(gè){代碼塊};例如

int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
    return num * multiplier;
};

解讀如下圖所示



如果沒有明確聲明返回值類型,則根據(jù)block代碼內(nèi)容推斷具體類型丙曙,例如

int multiplier = 7;

//未指明返回值類型
int (^myBlock)(int) = ^(int num) {
    return num * multiplier;
};

//指明返回值類型
int (^myBlock)(int) =  ^ int (int num) {
   return multiplier;
};

Blocks和變量

首先可以像函數(shù)一樣引用三種標(biāo)準(zhǔn)類型的變量:
1爸业、全局變量,包括靜態(tài)局部變量
2亏镰、全局函數(shù)
3扯旷、作用域內(nèi)的局部變量和參數(shù)
其次Blocks還支持另外兩種類型:
4、__block修飾的變量
5索抓、const導(dǎo)入的
所以Blocks內(nèi)部處理變量的五種情況:
1钧忽、全局變量(包括作用域內(nèi)的靜態(tài)局部變量)可以直接訪問
2毯炮、Blocks的參數(shù)可以直接訪問
3、作用域內(nèi)非靜態(tài)局部變量耸黑,作為const變量引用(只讀),強(qiáng)行修改編譯器報(bào)錯(cuò)
4桃煎、被__block修飾的作用域內(nèi)非靜態(tài)局部變量,可以直接訪問
5大刊、Blocks內(nèi)部聲明的局部變量为迈,可以直接訪問
示例代碼如下

int global_var = 1;

- (void)viewDidLoad {
[super viewDidLoad];
    
   
static int static_var = 2;
    
__block int loacal_var = 3;

__block int const_var = 3;

void (^myBlock)(int) = ^(int number) {

   global_var = global_var * 10;
   static_var = static_var * 10;
   loacal_var = loacal_var * 10;
   number     = number * 10;
   NSLog(@"局部變量:%d ,說(shuō)明局部變量可以讀取",const_var);
   NSLog(@"參數(shù)變量原:4 修改后%d缺菌,說(shuō)明可以正常訪問",number);

};
myBlock(4);

NSLog(@"全局變量原:1 修改后:%d葫辐,說(shuō)明可以正常訪問 ",global_var);
NSLog(@"靜態(tài)變量原:2 修改后:%d,說(shuō)明可以正常訪問 ",static_var);
NSLog(@"__block修飾的局部變量原:3 修改后:%d男翰,說(shuō)明可以正常訪問 ",loacal_var);
}

//輸出結(jié)果
局部變量:3 另患,說(shuō)明局部變量可以讀取
參數(shù)變量原:4 修改后40纽乱,說(shuō)明可以正常訪問
全局變量原:1 修改后:10蛾绎,說(shuō)明可以正常訪問 
靜態(tài)變量原:2 修改后:20,說(shuō)明可以正常訪問 
__block修飾的局部變量原:3 修改后:30鸦列,說(shuō)明可以正常訪問 

Blocks和對(duì)象

對(duì)象通過Properties來(lái)引用Blocks租冠。語(yǔ)法和定義Blocks語(yǔ)法類似;例如

@interface XYZObject : NSObject
@property (copy) void (^blockProperty)(void);
@end

self.blockProperty = ^{
   ...
};
self.blockProperty();

注意:此處應(yīng)該用copy薯嗤,因?yàn)閎lock需要被copy來(lái)保存引用的外界的變量顽爹,無(wú)需關(guān)心引用計(jì)數(shù)問題,編譯器自動(dòng)進(jìn)行管理骆姐。

還可以通過自定義類型簡(jiǎn)化

typedef void (^XYZSimpleBlock)(void);
 
@interface XYZObject : NSObject
@property (copy) XYZSimpleBlock blockProperty;
@end

避免循環(huán)引用

當(dāng)變量變成實(shí)例變量的時(shí)候镜粤,需要避免循環(huán)引用的問題。由于Blocks會(huì)持有使用的變量玻褪,比如在Viewcontroller中肉渴,block作為一個(gè)property被self持有,block中使用self調(diào)用方法带射,這樣便造成循環(huán)引用的問題使得二者在不使用時(shí)均得不到釋放同规。解決方法是在block中使用self的弱引用,用__weak修飾窟社。

@interface XYZBlockKeeper : NSObject
@property (copy) void (^block)(void);
@end

- (void)configureBlock {
    XYZBlockKeeper * __weak weakSelf = self;
    self.block = ^{
        [weakSelf doSomething];   // capture the weak reference
                                  // to avoid the reference cycle
    }
}

用法

作為變量

如果聲明Block作為一個(gè)變量券勺,可以像函數(shù)一樣調(diào)用。例如

int (^oneFrom)(int) = ^(int anInt) {
    return anInt - 1;
};
 
printf("1 from 10 is %d", oneFrom(10));
// Prints "1 from 10 is 9"
 
float (^distanceTraveled)(float, float, float) =
                         ^(float startingSpeed, float acceleration, float time) {
 
    float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);
    return distance;
};
 
float howFar = distanceTraveled(0.0, 9.8, 1.0);
// howFar = 4.9

作為函數(shù)參數(shù)

可以像傳遞其它參數(shù)一樣傳遞block灿里。大多數(shù)的情況不需要聲明关炼,只需要作為參數(shù)以內(nèi)聯(lián)的方式實(shí)現(xiàn)。例如

char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };

//根據(jù)首字符進(jìn)行一次排序 
qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) {
    char *left = *(char **)l;
    char *right = *(char **)r;
    //此函數(shù)表示:以參數(shù)3的長(zhǎng)度計(jì)算前2個(gè)參數(shù)的差值
    return strncmp(left, right, 1);
});

// Block implementation ends at "}"
 
// myCharacters is now { "Charles Condomine", "George", "TomJohn" }

作為方法參數(shù)

Cocoa Touch 提供了很多方法使用Blocks匣吊〉涟牵可以傳遞block作為參數(shù)使用跪楞。例如

//是否包含指定字符串
__block BOOL found = NO;
NSSet *aSet = [NSSet setWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil];
NSString *string = @"gamma";
 
[aSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
    if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {
        *stop = YES;
        found = YES;
    }
}];
 
// At this point, found == YES

作用

很大程度上起到了簡(jiǎn)化的作用。

簡(jiǎn)化回調(diào)

Blocks可以用處替換傳統(tǒng)回調(diào)的原因如下:
1侣灶、方法調(diào)用和回調(diào)代碼集中在一起甸祭;還常作為framework methods的參數(shù)。
2褥影、可以直接訪問局部變量池户。

簡(jiǎn)化枚舉

對(duì)一個(gè)數(shù)組進(jìn)行枚舉,常用的方法有3種
1凡怎、for循環(huán)
2校焦、for的泛型遍歷for (type *object in collection)
3、使用帶有block的簡(jiǎn)化方法统倒。例如- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block;

NSArray *array = @[@"A", @"B", @"C", @"A", @"B", @"Z", @"G", @"are", @"Q"];

//for循環(huán)
for (int i = 0; i < array.count; i ++)
{
   
   NSString *item = array[i];
   NSLog(@"index:%d value:%@",i,item);
}
 
 //泛型遍歷   
for (NSString *item in array)
{
   NSLog(@"value:%@",item);
}
 
 //枚舉方法   
[array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
   NSLog(@"index:%ld value:%@",idx,obj);
   
}];

簡(jiǎn)化并發(fā)任務(wù)

每個(gè)block都是獨(dú)立的工作單元寨典,包含可執(zhí)行代碼和保存使用的數(shù)據(jù),這使得它能夠在多線程開發(fā)中被異步調(diào)用房匆。
系統(tǒng)提供了一系列的多線程編程技術(shù)耸成,其中包括兩種任務(wù)調(diào)度機(jī)制:Operation queues 和 Grand Central Dispatch(GCD)。不像Thread關(guān)注怎么樣管理運(yùn)行浴鸿,把需要執(zhí)行的任務(wù)打包到blocks中井氢,然添加到隊(duì)列中,然后交給系統(tǒng)根據(jù)當(dāng)時(shí)的資源自動(dòng)執(zhí)行岳链。

簡(jiǎn)化Operation Queues

//定義任務(wù)
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
    ...
}];

// 主線程執(zhí)行
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperation:operation];
 
// 后臺(tái)執(zhí)行
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];

更多知識(shí)請(qǐng)閱讀Operation Queues

簡(jiǎn)化Grand Central Dispatch

//獲取并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//定義任務(wù)添加到隊(duì)列并執(zhí)行
dispatch_async(queue, ^{
    NSLog(@"Block for asynchronous execution");
});

更多知識(shí)請(qǐng)閱讀 Dispatch Queues

參考文獻(xiàn):Blocks Programming Topics花竞、Working with Blocks

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市掸哑,隨后出現(xiàn)的幾起案子约急,更是在濱河造成了極大的恐慌,老刑警劉巖苗分,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厌蔽,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡俭嘁,警方通過查閱死者的電腦和手機(jī)躺枕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)供填,“玉大人拐云,你說(shuō)我怎么就攤上這事〗” “怎么了叉瘩?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)粘捎。 經(jīng)常有香客問我薇缅,道長(zhǎng)危彩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任泳桦,我火速辦了婚禮汤徽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘灸撰。我一直安慰自己谒府,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布浮毯。 她就那樣靜靜地躺著完疫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪债蓝。 梳的紋絲不亂的頭發(fā)上壳鹤,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音饰迹,去河邊找鬼芳誓。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蹦锋,可吹牛的內(nèi)容都是我干的兆沙。 我是一名探鬼主播欧芽,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼莉掂,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了千扔?” 一聲冷哼從身側(cè)響起憎妙,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎曲楚,沒想到半個(gè)月后厘唾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡龙誊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年抚垃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片趟大。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鹤树,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出逊朽,到底是詐尸還是另有隱情罕伯,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布叽讳,位于F島的核電站追他,受9級(jí)特大地震影響坟募,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜邑狸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一懈糯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧单雾,春花似錦昂利、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至硬萍,卻和暖如春扩所,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背朴乖。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工祖屏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人买羞。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓袁勺,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親畜普。 傳聞我的和親對(duì)象是個(gè)殘疾皇子期丰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

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

  • 在編程領(lǐng)域里,一個(gè)牛逼程序員和一個(gè)二逼程序員之間的區(qū)別主要是其對(duì)所用編程語(yǔ)言優(yōu)秀特性的運(yùn)用方式吃挑。要說(shuō)到Object...
    Jimmy_L_Wang閱讀 467評(píng)論 0 1
  • Blocks編程要點(diǎn) 目錄 簡(jiǎn)介............................................
    xuejunjun閱讀 1,218評(píng)論 0 5
  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來(lái)就是把...
    Dove_iOS閱讀 27,135評(píng)論 30 470
  • 天秤座真的是糾結(jié)病的祖宗舶衬。埠通。。說(shuō)服自己需要幾光年距離的行走時(shí)間逛犹。端辱。。 不管怎么樣虽画,今天給Department He...
    匿稱也不行閱讀 203評(píng)論 0 0
  • 你能不能晚上乖乖睡覺舞蔽?
    小寶尼閱讀 166評(píng)論 0 0