美文网首页
iOS中block的使用

iOS中block的使用

作者: 是我始终拒绝成长吗 | 来源:发表于2015-08-20 13:41 被阅读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引用地址释放时的野指针错误。

    相关文章

      网友评论

          本文标题:iOS中block的使用

          本文链接:https://www.haomeiwen.com/subject/fubgxttx.html