美文网首页iOS开发
iOS基础之block

iOS基础之block

作者: ValienZh | 来源:发表于2016-05-16 09:46 被阅读586次

    1.block类型-存储代码块的类型

    在异步编程时常需要进行函数回调,在C#中会用匿名委托或者lambda表达式讲一个操作作为参数进行传递.
    ObjC中是使用对于闭包的实现,在块状中我们可以持有或引用局部变量. 同时利用Block可以将一个操作作为参数进行传递;

    blcok用法:

    • 定义:返回值类型 ( ^变量名 ) ( 形参类型 );
    • 赋值:变量名=^(形参){
      代码块+形参变量
      };
    • 使用:变量(实参);

    例:

        int (^myBlcok)(int ,int)=^(int m,int n){
          return  m+n;
          }; //无参数时大括号前()可省略
          
          myBlock(10,5);    //调用块,省略了接受块返回值;
    

    总结:经过简单了解C与OC;发现从最小的一个变量到表达式再到一个函数,其实只起两点作用: 值(返回值) 与 功能(行为,方法,作用).
    所以说一行代码,按它是使用了值 还是 功能来解读比较容易理解.

    Block做使用场景:

    • 如果回调方法比较少,1~2,最好不要超过3个,这个时候使用block比较合适
    • 如果回调方法非常多,同时又不用每一个方法都必须实现,这个时候用delegate会比较方便!

    block传值的循环引用问题:

    只有当block直接或间接的被self持有时,在block使用self时才需要替换为weak self。如果在 Block 内需要多次 访问 self,则需要使用 strongSelf。

       __weak __typeof__(self) weakSelf = self;
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    
            __strong __typeof(self) strongSelf = weakSelf;
    
            [strongSelf doSomething];
    
            [strongSelf doOtherThing];
    
        });
    

    typedef block格式

    类似函数指针,直接在定义格式之前加 typedef关键字,之后变量名就是类型的别名了.
    typedef viod (^别名)(形参);
    一般以后需要使用block作为函数方法的参数时,为方便最好用别名.而在block作用返回值时,一定需要别名,因为编译器不能识别做此时类型做何种解释.
    延伸:经过测试,block在编译时按代码顺序,而运行时按调用顺序(变量作用域)

    使用例子:
    KCButton.h

    #import <Foundation/Foundation.h>
    @class KCButton;
    typedef void(^KCButtonClick)(KCButton *);
    
    @interface KCButton : NSObject
    
    #pragma mark - 属性
    @property (nonatomic,copy) KCButtonClick onClick;
    
    #pragma mark 点击方法
    -(void)click;
    @end
    

    KCButton.m

    #import "KCButton.h"
    
    
    @implementation KCButton
    
    -(void)click{
        NSLog(@"Invoke KCButton's click method.");
        if (_onClick) {
            _onClick(self);
        }
    }
    
    @end
    

    main.m

       KCButton *button=[[KCButton alloc]init];
        button.onClick=^(KCButton *btn){
            NSLog(@"Invoke onClick method.The button is:%@.",btn);
        };
        [button click];
        /*结果:
         Invoke KCButton's click method.
         Invoke onClick method.The button is:<KCButton: 0x1006011f0>.
         */
    

    block访问外部变量

    • block内部可以访问外部局部变量,但是此时是const copy方式,地址不同,相当于值传递,只读的.如果外部定义时加前缀__block时,内部可改变外部局变值.
    • block内部如果创建了和外部同名的变量,会屏蔽外部作用域.此时内部的变量也存在栈区;
      原因:block本质是代码块,ARC下创建的时候在堆区,此时代码只是单纯储存,没有功能;当调用的时候,相当于代码增加到main中,这样代码块中创建的变量就跟正常的一样; (block调用完成内部变量即释放,而堆区的只在释放block时一起释放).
    • 如果是静态变量(static修饰局变,生命周期延长,存储在数据区(同初始化的全局))和全局变量.地址传递.此时block存储在全局区.
    • 常量字符串@"abc",加__block会引用常量变量(如:a变量,a = @"abc",内部可以任意修改a 指向的内容)的地址。不加block就是@"abc"本身地址,不可变;

    三种类型block

    根据block在内存中的位置
    "NSGlobaBlock"类似函数,存于代码区--全局block
    "NNStackBlock"栈区,函数返回后的Block--栈
    "NSMallocBlock"堆block--堆
    
    1. block内没有使用外部变量或是只使用了全局/静态变量时.存于全局代码区,为全局block;---(ARC和MRC下一致)
    2. 当使用外部变量时
      • MRC下,block代码存于栈区;如果此外部变量A存于区,那么A会被copy到block分配的区;如果A是存于区,那么A在block块内与快外相同.
      • ARC下,block代码存于堆区.如果此外部变量A存于区,那么A会被copy到block分配的区;如果A是存于区,那么A在block块内与快外相同.
    3. 如果需要修改外部变量,需要在变量前面声明__Block;
      当使用下划线Block修饰外部变量时:
      • MRC下,无论变量A存于还是区,A在block块内与快外相同;
      • ARC下,如果此外部变量A存于区,那么A会被转移而不是复制区;如果A是存于区,那么A在block块内与快外相同.

    面试题:block的@property参数(内存管理参数)为什么要用copy:如果不用copy,此时不论ARC还是MRC都是栈Bolck,栈block会提前释放,导致无法继续使用;可以copy到堆区手动管理内存.(而字符串copy是防止字符串如果是非常量的,外部可变,造成非预估的结果;)

    block在MRC下得内存隐患(NNStacKBlock)

    Block_copy将block及内部变量拷贝到堆区.
    使用完毕用Blok_release(block变量)释放此堆区空间;


    block使用技巧

    1. block结构快速显示:inlineBlock...(也可右下角自定义快速显示其他格式)
    2. Block作为方法参数时,最好把参数列表部分加上,这样后面调用方法时,会自动有格式;
    3. 做方法参数时,需要加上返回值类型;
    4. 做返回值时,先定义别名,最后别忘记执行返回值;
    5. 方法中,void(^)()表示block类型同int,做参和返回值;做实例变量
      @property (nonatomic, copy) void(^变量名)()
    6. get点语法获取block类型实例变量时,自动执行,后面需加();

    相关文章

      网友评论

        本文标题:iOS基础之block

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