美文网首页
iOS Blocks语法、变量及为什么选用copy修饰符

iOS Blocks语法、变量及为什么选用copy修饰符

作者: 明若晴空 | 来源:发表于2018-05-31 15:35 被阅读5次

    Blocks是C语言的扩充功能:带有自动变量(局部变量)的匿名函数;

    匿名函数就是没有名称的函数。

    1、Block语法和Block变量

    C语言中的变量(根据作用域不同区分的):

    1、自动变量(局部变量);

    2、函数的参数;

    3、静态变量(静态局部变量);

    4、静态全局变量;

    5、全局变量;

    其中,静态变量(静态局部变量)、静态全局变量、全局变量是可以在函数的多次调用之间传递的变量。

    Block语法,形式如下:(^ 返回值类型 参数列表 表达式)

    ^(void)  (int event)

    {

          Printf(“buttonId:%d event=%d\n”, i, event);

    }

    Block语法与一般C语言函数定义相比,有两点不同:

    1、没有函数名;

    2、带^;

    ^:插入记号,便于查找

    Block语法可以省略返回值类型;也可以省略参数列表类型;

    省略返回值类型和参数列表类型后的语法格式为:^{}

    函数指针的声明示例:

    int (*funcptr) (int) = &func;

    Block类型变量的示例:

    int (^blk) (int)

    Block变量和一般的C语言变量完全相同,可作为以下用途使用:

    1、自动变量;

    2、函数参数;

    3、静态(局部)变量;

    4、静态全局变量;

    5、全局变量;

    2、截获自动变量值

    demo如下:

    int i = 10;

    NSString *str = @"hello";

    void (^blk) (void) = ^{

        NSLog(@"str = %@, i = %d", str, i);

    };

    str = @"I'm not ok";

    i = 11;

    blk();

    NSLog(@"end");

    最后打印的日志如下:

    从上面的日志可以看出:Block语法使用的是之前定义时的自动变量的值,也就是截获自动变量的值,保存起来,在执行时使用。即使在block定义和执行期间修改过这些自动变量的值,也不会影响,它还是执行之前定义时所截获的自动变量值。

    这就是自动变量值的截获。

    3、__block说明符

    int i = 10;

    NSString *str = @"hello";

    void (^blk) (void) = ^{

        i = 12;

        NSLog(@"str = %@, i = %d", str, i);

    };

    str = @"I'm not ok";

    i = 11;

    blk();

    NSLog(@"end");

    这个时候,i = 12会报错:

    Variable is not assignable (missing __block type specifier)

    如果将i声明为__block int i = 10;,就可以实现在block中对i进行赋值。

    这种变量称为__block变量。

    4、Block循环引用

    避免循环引用的方法有三种:

    1、使用__block

    2、使用__weak

    3、使用__unsafe_unretained

    使用__block修饰符的优点:

    1、可以动态的控制对象的持有周期;(在执行block时,可动态的决定是都将nil或者其他对象赋值给__block变量)

    2、在ARC和MRC都可以使用;

    3、可以修饰对象,也可以修饰基本数据类型;

    使用__block修饰符的缺点:

    1、为了避免循环引用,必须执行Block;如果只执行了Block语法,却不执行Block的使用时,无法避免循环使用。

    __weak修饰的自动变量:不能在Block中被赋值;只能修饰对象,不能修饰基本数据类型。

    unsafe_unretained是在iOS4以及OS X Snow Leopard的环境下,也就是不能使用weak的环境下使用

    5、copy/release

    ARC无效时,

    Block从栈复制到堆,是通过copy实例方法实现;

    手动释放Block,通过release实例方法来来释放;

    在堆上,可通过retain实例方法持有;

    而在栈上,使用retain方法持有是无效的;

    因此推荐使用copy实例方法来持有Block,copy方法可实现将Block从栈复制到堆上。

    默认情况下,Block创建时是在栈上的。

    ARC无效时,__block可以被用来避免Block的循环使用。

    因为,当Block从栈复制到堆上时,如果Block中使用的自动变量,有block说明符,那么就不会被retain。如果Block中使用的自动变量,没有block说明符,那么就会被retain。

    注意:__block修饰符在ARC有效和无效时,区别很大,因此使用需注意。

    6、为什么要选用copy来修饰Block?

    首先,Block也是OC对象。

    Block一共有三种类型:

    1)、全局Block( _NSGlobalBlock):设置在程序的数据区(.data区);

    2)、栈中的Block( _NSStackBlock):设置在栈上的;

    3)、堆中的Block( _NSMallocBlock):设置在由malloc函数分配的内存块(堆)中。

    1)、全局Block对象

    有两种情况:

    (1)在记述全局变量的地方存在Block语法:因为在使用全局变量的地方不能使用自动变量,所以不存在对自动变量的截获,因此Block语法的内容也不会依赖于执行时的状态,就可以设置在与全局变量相同的数据区域,因此成为全局Block对象;(不截获)

    (2)Block语法中的表达式,可能会截获自动变量的值,但是只要不使用截获的自动变量,这样的Block也是全局Block;(截获但不使用)

    2)、栈中的Block

    除了上面两种情况之外生成的Block都是_NSStackBlock对象,设置在栈上。

    3)、堆中的Block

    配置在全局变量上的Block,从变量的作用域外也可以通过指针安全的使用。

    设置在栈上的Block,如果其所属的变量的作用域结束,这个Block也会被废弃。block变量也是配置在栈上的,如果其所属的变量的作用域结束,这个block变量也会被废弃。

    Block提供了将Block和__block变量从栈上复制到堆上的方法来解决问题,这样“即使Block语法记述的变量作用域结束,堆上的Block也可以继续存在”。

    在ARC有效时,编译器大多数情况下会恰当的进行判断,自动生成将Block从栈上复制到堆上的代码。比如,将Block作为函数返回值返回时,编译器会自动生成复制到堆上的代码。

    但是有些情况下,需要手动的使用copy方法生成代码,来将Block从栈上复制到堆上。

    这些不能自动生成复制代码的情况有:

    1、通过方法或函数的参数传递Block时,需要手动复制。

    但是如果在方法或者函数中适当的复制了传递过来的参数,就不必在调用之前手动复制了。

    比如在Cocoa框架的方法名中含有usingBlock的方法(如NSArray类的enumerateObjectsUsingBlock方法),再比如Grand Central Dispatch的API(如dispatch_async函数)。

    如果不想判断这些情况,可以在所有情况下都进行copy,但是Block从栈上复制到堆上是非常消耗CPU的。

    对于已经在堆上的Block或者全局Block调用copy方法,不会引起任何问题。

    对于全局的Block进行copy时,什么也不做;

    对于堆上的Block进行copy时,Block的引用计数加1.

    相关文章

      网友评论

          本文标题:iOS Blocks语法、变量及为什么选用copy修饰符

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