iOS中block的使用

2015-08-20  本文已影响735人  是我始终拒绝成长吗

#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引用地址释放时的野指针错误。

上一篇下一篇

猜你喜欢

热点阅读