Block用法总结

一、 block语法格式,如下

//return type (^BlockName)(list of arguments) = ^return type (list of arguments){do something;};

根据上面的英文应该就能理解各部分是什么了,接下来是一个简单的例子,可以帮助理解,这里引用了  Sindri的小巢(简书作者)的文章

原文链接://www.greatytc.com/p/29d70274374b

int (^sumOfNumbers) (int a, int b) = ^int(int a, int b) {

return a + b;

};

//Block的调用

int result = sumOfNumbers(100, 200); //无参时,也要带上小括号

NSLog(@"result = %d", result);

1.等号左侧声明一个名为sumOfNumbers的代码块,名称前用^符号表示后面的字符串是block的名称;

2.最左侧的int表示这个block的返回值;

3.括号中间表示这个block的参数列表,这里接收两个int类型的参数。

4. 而在等号右侧表示这个block的实现,其中返回值类型是可以省略的,编译器会根据上下文自动补充返回值类型。

5.使用^符号衔接着一个参数列表,使用括号包起来,告诉编译器这是一个block,然后使用大括号将block的代码封装起来。

二、 block的直接使用(匿名Block对象)

NSArray *array = @[@"3", @"1", @"2"];

array = [array sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {

return[obj1 compare: obj2];

}];

NSLog(@"array = %@", array);

其实理解起来也很简单,匿名的Block对象/是可以传递给/方法的Block对象/的,而不需要先赋值给变量。

让我们先看看匿名的整数。有三种方法可以将整数传递给方法:

方法1: 声明、赋值和使用完全分开

int i;

i = 5;

NSNumber *num = [NSNumber numberWithInt:i];

方法2: 在一行中声明赋值使用

int i = 5;

NSNumber *num = [NSNumber numberWithInt:i];

方法3:跳过变量声明步骤

NSNumber *num = [NSNumber numberWithInt:5];

如果采用第三种方法,就是匿名地传递一个整数。因为它没有名字,所以说它是匿名的。

而将Block对象传递给方法的办法和传递整数相同。分别用三行代码来声明Block对象,然后赋值,最后使用。但是匿名传递Block对象更加常用。

三、类型重定义(block重命名)

Block对象的语法可能会比较复杂。通过使用第11章介绍过的typedef关键字,可以将某个Block对象类型定义为一个新类型,以方便使用。需要注意的是,不能在方法的实现代码中使用typedef。也就是说,应该在实现文件的顶部,或者头文件内使用typedef。在main.m中,添加以下代码:

#import

typedef int (^SumBlock)(int a, int b);

int main (int argc, const char * argv[])

{

这段代码中的typedef语句看上去与Block变量声明很像,但是,这里定义的是一个新的类型,而不是变量。跟在^字符后面的是类型名称。创建这个新类型后,就能简化相应Block对象的声明。

现在使用新的类型声明SumBlock:

注意,这里的Block类型只是声明了Block对象的实参和返回类型,并没有实现真正的Block对象。

SumBlock sumBlock = ^(int a, int b){

return a + b;

};

int result = sumBlock(100, 200);

NSLog(@"result = %d", result);

四、外部变量 —— block对栈区变量做只读拷贝操作,使用的是对变量的copy,而不是变量本身

Block对象通常会(在其代码中)使用外部创建的其他变量(基本类型的变量,或者是指向其他对象的指针)。这些外部创建的变量叫做外部变量(external variables)。当执行Block对象时,为了确保其下的外部变量能够始终存在,相应的Block对象会捕获(captured)这些变量。对基本类型的变量,捕获意味着程序会拷贝变量的值,并用Block对象内的局部变量保存。对指针类型的变量,Block对象会使用强引用。这意味着凡是Block对象用到的对象,都会被保留。所以在相应的Block对象被释放前,这些对象一定不会被释放(这也是Block对象和函数之间的差别,函数无法做到这点)。

使用外部变量

int a = 200, b = 100;

int (^minusBlock) (void) = ^(void){return a - b;};

NSLog(@"minus1 = %d", minusBlock());

//结果为100

a = 500, b = 200;

NSLog(@"minus2 = %d", minusBlock());

//结果还是100

只需要在定义外部变量的时候,使用 __block 或者 static 修饰。

__block int a = 200, b = 100;//或者 static

int (^minusBlock) (void) = ^(void){return a - b;};

NSLog(@"minus1 = %d", minusBlock());

//结果为100

a = 500, b = 200;

NSLog(@"minus2 = %d", minusBlock());

//结果是300

修改外部变量

在Block对象中,被捕获的变量是常数,程序无法修改变量所保存的值。如果需要在Block对象内修改某个外部变量,则可以在声明相应的外部变量时,在前面加上__block关键字,否则,如果在block内部改变外部变量的值程序就会报错

这样写是没问题的

void(^oneBlock)(void) = ^(void){

int n = 1;

n = 2;

NSLog(@"n = %d", n);

};

oneBlock();//结果是2

下面的写法会报错

int n = 1;

//定义Block

void(^oneBlock)(void) = ^(void){

n = 2;//报错!!!!!!

NSLog(@"n = %d", n);

};

oneBlock();

同样可以使用__block修饰外部变量来解决这个问题(使用static修饰或定义成实例变量的方法解决)

__block int n = 1;

//定义Block

void(^oneBlock)(void) = ^(void){

n = 2;

NSLog(@"n = %d", n);

};

oneBlock();//结果是2

在block对象中使用self

如果需要写一个使用self的Block对象,就必须要多做几步工作来避免循环引用问题。下面举个简单的例子

self.nameLabel.text = @"xiaoming";

void(^strBlock)(void) = ^(void){

NSString*name = self.nameLabel.text;

NSLog(@"%@", name);

};

strBlock();

Block中使用了self,这个Block对象会捕获self,Block必须等到所引用的对象全部释放后才会释放,然而,self又要到程序运行结束才释放,这样Block就不可能得到释放,就陷入强引用循环了。

为了打破这个强引用循环,可以先在Block对象外声明一个__block指针(ARC下使用__weak),然后将这个指针指向Block对象使用的self,最后在Block对象中使用这个新的指针:

__weak RootViewController *weakSelf = self; // 一个弱引用指针

void(^strBlock)(void) = ^(void){

NSString*name =weakSelf.nameLabel.text;

NSLog(@"%@", name);

};

如果想要了解的更深入,可以继续看完下面的内容,举例有所变化,但应该可以理解。

=======================================

现在这个Block对象对BNREMployee实例是弱引用,强引用循环打破了。

然而,由于是弱引用,所以self指向的对象在Block执行的时候可能会被释放。

为了避免这种情况的发生,可以在Block对象中创建一个对self的局部强引用:

__weak BNREmployee *weakSelf = self; // 弱引用

myBlock = ^{

BNREmployee *innerSelf = weakSelf; // 局部强引用

NSLog(@"Employee: %@", innerSelf);

};

通过创建innerSelf强引用,就可以在Block和BNREmployee实例中再次创建一个强引用循环。但是,由于innerSelf引用是针对Block内部的,所以只有在Block执行的时候它才会执行,而Block结束之后就会自动消失。

每次写Block对象的时候都引用self会是一个很好的练习。

在Block对象中无意使用self,而是使用了实例变量的情况

如果直接在Block对象中使用实例变量,那么block会捕获self,而不会捕获实例变量。这是实例变量的一个鲜为人知的特点。例如,以下这段代码直接存取一个实例变量:

__weak BNREmployee *weakSelf = self;

myBlock = ^{

BNREmployee *innerSelf = weakSelf; // 局部强引用

NSLog(@"Employee: %@", innerSelf);

NSLog(@"Employee ID: %d", _employeeID);

};

编译器是这么解读这段代码的:

__weak BNREmployee *weakSelf = self;

myBlock = ^{

BNREmployee *innerSelf = weakSelf; // 局部强引用

NSLog(@"Employee: %@", innerSelf);

NSLog(@"Employee ID: %d", self->_employeeID);

};

->语法看上去是不是很熟悉?这个语法实际是用来后去堆上的成员结构的。从最底层来说,对象实际就是结构。

由于编译器将_employeeID看成是self->_employeeID,self就被Block对象无意地捕获了。这样又会造成之前使用weakSelf和innerSelf避免的强引用循环。

怎样解决呢?不要直接存取实例变量。使用存取方法!

__weak BNREmployee *weakSelf = self;

myBlock = ^{

BNREmployee *innerSelf = weakSelf; // 局部强引用

NSLog(@"Employee: %@", innerSelf);

NSLog(@"Employee ID: %d", innerSelf.employeeID);

};

现在没有直接地使用self了,就不会造成无意识地强引用循环。

在这种情况下,重要的是要理解编译器是如何思考的,这样才能避免隐藏的强引用循环。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353