iOS中block的使用

#Block

###Block概述

Block它是C语言级别和运行时方面的一个特征。Block封装了一段代码逻辑,也用{}括起,和标准C语言中的函数/函数指针很相似,此外就是blokc能够对定义环境中的变量可以引用到。这一点和其它各种语言中所说的“闭包”是非常类似的概念。在iOS中,block有很多应用场景,比如对代码封装作为参数传递。这在使用dispatch并发(Operation中也有BlockOperation)和completion异步回调等处都广泛应用。

-Block是苹果官方特别推荐使用的数据类型,使用场景比较广泛

-动画

-多线程

-集合遍历

-网络请求回调

-Block的作用

-用来保存某一段代码,可以在恰当时候再去出来调用

-功能类似于函数和方法

1.Block的基本使用

```

声明:(返回类型)(^声明的block名称)(参数列表);

实现:^(参数列表){代码块}

返回值类型(^block变量名)(参数列表) = ^(形参列表) {

};

```

赋值的例子:

```objc

intsum(intvalue1,intvalue2)

{

returnvalue1 + value2;

}

intmain(intargc,constchar* argv[]) {

@autoreleasepool {

int(*sumP)(int,int);

sumP = sum;

NSLog(@"sum = %i",sumP(10,20));

int(^sumBlock) (intvalue1,intvalue2) = ^(intvalue1,intvalue2) {

returnvalue2 + value1;

};

NSLog(@"sum----%i",sumBlock(10,30));

}

return0;

}

```

其实Block和C语言中的指向函数的指针差不多

其实我们完全可以定义C语言的函数,如下所示

```objc

intsum(intvalue1,intvalue2)

{

returnvalue1 + value2;

}

intmain(intargc,constchar* argv[]) {

@autoreleasepool {

int(*sumP)(int,int);

sumP = sum;

NSLog(@"sum = %i",sumP(10,20));

}

return0;

}

```

有时为了方便,用typedef给出定义,在苹果的官方文档里,建议出现多次的block使用typedef定义。

而block的调用,则十分简单:block名称(参数列表);

2.使用中的注意点

-block中可以定义和外界同名的变量,并且如果在block中定义了和外界同名的变量,在block中访问的是block内部的变量

-默认情况下,在block内部不能改变外面变量的值

-因为block中变量和外面的变量并不是同一个变量,可以通过打打印变量的地址看出来

-如果block中访问到了外界的变量,block会将外界变量拷贝一份到堆内存中

-因为block中使用的外面变量的值是拷贝过来的即值拷贝,所以在调用之前修改外界变量的值,不会影响到block中拷贝的值

-如果想在block中修改外界变量的值,必须在外界变量前面加上`__block`

-如果在block中修改了外界变量的值,会影响到外界变量的值,

我们可以把.m反编译成c++文件,看看其内部是怎么实现`__block`这个关键词可以修改外部变量的

```cpp

intsum(intvalue1,intvalue2)

{

returnvalue1 + value2;

}

struct__main_block_impl_0 {

struct__block_impl impl;

struct__main_block_desc_0* Desc;

__main_block_impl_0(void*fp,struct__main_block_desc_0 *desc,intflags=0) {

impl.isa = &_NSConcreteStackBlock;

impl.Flags = flags;

impl.FuncPtr = fp;

Desc = desc;

}

};

staticint__main_block_func_0(struct__main_block_impl_0 *__cself,intvalue1,intvalue2) {

returnvalue2 + value1;

}

staticstruct__main_block_desc_0 {

size_t reserved;

size_t Block_size;

} __main_block_desc_0_DATA = {0,sizeof(struct__main_block_impl_0)};

intmain(intargc,constchar* argv[]) {

/* @autoreleasepool */{ __AtAutoreleasePool __autoreleasepool;

int(*sumP)(int,int);

sumP = sum;

NSLog((NSString *)&__NSConstantStringImpl__var_folders_8b_skbhgyln78718lwjds4398k80000gn_T_main_185299_mi_0,sumP(10,20));

int(*sumBlock) (intvalue1,intvalue2) = ((int(*)(int,int))&__main_block_impl_0((void*)__main_block_func_0, &__main_block_desc_0_DATA));

NSLog((NSString *)&__NSConstantStringImpl__var_folders_8b_skbhgyln78718lwjds4398k80000gn_T_main_185299_mi_1,((int(*)(__block_impl *,int,int))((__block_impl *)sumBlock)->FuncPtr)((__block_impl *)sumBlock,10,30));

}

return0;

}

staticstructIMAGE_INFO {unsignedversion;unsignedflag; } _OBJC_IMAGE_INFO = {0,2};

```

加上__block

```cpp

intsum(intvalue1,intvalue2)

{

returnvalue1 + value2;

}

struct__Block_byref_a_0 {

void*__isa;

__Block_byref_a_0 *__forwarding;

int__flags;

int__size;

inta;

};

struct__main_block_impl_0 {

struct__block_impl impl;

struct__main_block_desc_0* Desc;

__Block_byref_a_0 *a;// by ref

__main_block_impl_0(void*fp,struct__main_block_desc_0 *desc, __Block_byref_a_0 *_a,intflags=0) : a(_a->__forwarding) {

impl.isa = &_NSConcreteStackBlock;

impl.Flags = flags;

impl.FuncPtr = fp;

Desc = desc;

}

};

staticint__main_block_func_0(struct__main_block_impl_0 *__cself,intvalue1,intvalue2) {

__Block_byref_a_0 *a = __cself->a;// bound by ref

(a->__forwarding->a) =20;

returnvalue2 + value1;

}

staticvoid__main_block_copy_0(struct__main_block_impl_0*dst,struct__main_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a,8/*BLOCK_FIELD_IS_BYREF*/);}

staticvoid__main_block_dispose_0(struct__main_block_impl_0*src) {_Block_object_dispose((void*)src->a,8/*BLOCK_FIELD_IS_BYREF*/);}

staticstruct__main_block_desc_0 {

size_t reserved;

size_t Block_size;

void(*copy)(struct__main_block_impl_0*,struct__main_block_impl_0*);

void(*dispose)(struct__main_block_impl_0*);

} __main_block_desc_0_DATA = {0,sizeof(struct__main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

intmain(intargc,constchar* argv[]) {

/* @autoreleasepool */{ __AtAutoreleasePool __autoreleasepool;

int(*sumP)(int,int);

sumP = sum;

NSLog((NSString *)&__NSConstantStringImpl__var_folders_8b_skbhgyln78718lwjds4398k80000gn_T_main_9f1f9e_mi_0,sumP(10,20));

__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a,0,sizeof(__Block_byref_a_0),10};

int(*sumBlock) (intvalue1,intvalue2) = ((int(*)(int,int))&__main_block_impl_0((void*)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a,570425344));

NSLog((NSString *)&__NSConstantStringImpl__var_folders_8b_skbhgyln78718lwjds4398k80000gn_T_main_9f1f9e_mi_1,((int(*)(__block_impl *,int,int))((__block_impl *)sumBlock)->FuncPtr)((__block_impl *)sumBlock,10,30));

}

return0;

}

staticstructIMAGE_INFO {unsignedversion;unsignedflag; } _OBJC_IMAGE_INFO = {0,2};

```

很明显加上了__block之后传入的是&这样的,是把地址传到block内部去了,所以可以修改外部变量了

-block是存储在堆中还是栈中呢

-默认情况下block是存储在栈中的,如果对block进行一个copy操作,block会转移到堆中

-如果block在栈中,block中访问了外界的对象,那么不会对对象进行retain操作

-但是如果block在堆中,block中访问了外面的对象,那么会对外界的对象进行一次retain操作

-如果block中访问了外界的对象,一定要给对象加上__block,只要加上__block,哪怕block在堆中,也不会对外界的对象进行retain

-想把block弄到堆中可以使用`Clock_copy()`

###有如下一些注意点:

对定义环境的变量使用。默认是以const的方式使用,这有点像函数的const参数传递,如过需要block内修改可变,则使用__block,这样做存储就实现了共享,包括块中的递归应用和定义环境上下文中的多个block使用。block通常定义在栈帧当中,而当所处的栈帧被销毁的时候,block以及引用到的__block变量将会依然有效。

引用类型问题。block中的引用默认都是强引用,必要的时候需要使用__weak,同delegate使用的注意一样,避免循环引用。此外,苹果文档中还给出了对instanceVariable和对localVariable引用不同的例子,注意体会下。

copy。在类属性中,要使用copy。此外,对block进行copy要使用Block_copy()/Block_release()。

苹果文档中几种需要避免使用的方式。《Blocks Programming Topics》中Using Blocks中的例子,实际上就是要注意block定义的位置与其上下文的关系。

有关Block的地址/引用。注意这篇文章中最后例子中的问题:http://www.cnblogs.com/kesalin/archive/2013/04/30/ios_block.html我的理解是Block的地址发生了变化,最终的问题是对Block引用地址释放时的野指针错误。

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

推荐阅读更多精彩内容