美文网首页
iOS - 初识 block

iOS - 初识 block

作者: valentizx | 来源:发表于2016-06-02 21:59 被阅读74次

    block定义

    • 格式:

    返回类型 (^block名字) (参数列表);

    • 同C语言的定义函数指针一样,C语言的函数指针定义格式:

    返回类型 (*指针名字) (参数列表);

    block赋值

    名字 = ^{xxx};

    • 如同普通的变量赋值一样类型、分号都需要‘一一对应’,如同int a = 5;^标志右边的代码段是block类型。

    block调用

    block名字 (参数列表);

    例(无参):

    //定义
     void (^valenti) ();
    //赋值
     valenti = ^{
            NSLog(@"VaLenTi is MEEEEE!");
        };
    //调用
     valenti();
    

    例(有参):

    //定义
     void (^valenti) (NSString* name);
    //赋值
     valenti = ^(NSString* name){
            NSLog(@"VaLenTi is %@!",name);
        };
    //调用
     valenti(@"ME");
    

    例(有参有返回值):

    //定义
     NSInteger (^sum)(NSInteger value1, NSInteger value2);
    //赋值    
     sum = ^(NSInteger value1, NSInteger value2){
            return value1 + value2;
        };
    //调用
     NSInteger result = sum(1,1);
    

    typedef与block

    如若有这样的需求:定义四个block实现两个参数的加减乘除,他们的代码是如下这样的:

        //加
        NSInteger (^add)(NSInteger value1, NSInteger value2);
        add = ^(NSInteger value1, NSInteger value2){
            return value1 + value2;
        };
        NSInteger result = add(1,1);
       
        //减
        NSInteger (^sub)(NSInteger value1, NSInteger value2);
        sub = ^(NSInteger value1, NSInteger value2){
            return value1 - value2;
        };
        NSInteger result2 = sub(1,1);
        
        //乘
        NSInteger (^mul)(NSInteger value1, NSInteger value2);
        mul = ^(NSInteger value1, NSInteger value2){
            return value1 * value2;
        };
        NSInteger result3 = mul(1,1);
        
        //除
        NSInteger (^div)(NSInteger value1, NSInteger value2);
        div = ^(NSInteger value1, NSInteger value2){
            return value1 / value2;
        };
        NSInteger result4 = div(1,1);
    

    可见,除了block的名字和操作不同以外,其余的结构都是相同的,那么,相同的部分即可用typedef起别名的形式“抽取”出来。格式如下:

    typedef 返回值类型 (^block名字) (参数列表)

    • 同C语言的函数指针别名一样,名称即代表别名。

    上述代码可改为:

    //1.在类扩展处定义别名
    typedef NSInteger (^calculate) (NSInteger value1,NSInteger value2);
    //2.在实现中定义对应功能的block代码并赋值
    //加
    calculate add = ^(NSInteger value1, NSInteger value2){
        return value1 + value2;
     };
     NSInteger result = add(1,1);
    //减
    calculate sub = ^(NSInteger value1, NSInteger value2){
        return value1 - value2;
    };
    NSInteger result2 = sub(1,1);
    //乘
    calculate mul = ^(NSInteger value1, NSInteger value2){
        return value1 * value2;
    };
    NSInteger result3 = mul(1,1);
    //除
    calculate div = ^(NSInteger value1, NSInteger value2){
        return value1 / value2;
    };
    NSInteger result4 = div(1,1);
    

    注意事项

    1. block可以访问外部变量,例:
    int a = 10;
    void (^block)() = ^{
          NSLog(@"%zd",a);
    }
    
    1. block中可以定义和外界同名的变量,在block内部外部存在同名变量的情况下,block访问的变量是内部变量-“就近原则”。

    2. 默认情况下,不可以在block内部修改外部的变量,1中block内部是不可以对a进行赋值的,因为block中的a和外部的a本质上并不是同一个a,block访问的外部变量会将外部的变量拷贝一份到堆内存中,验证:

    int a = 5;
    NSLog(@"%p",&a);
    void (^block)()= ^{
          NSLog(@"%p",&a);
    };
    block();
    

    结果是:

    0x7fff53f42a2c
    0x7fb601475220

    • 如果想在block中修改外界变量的值,必须在外界变量前面加上__block,在内部修改了变量的值会直接影响外部的值,但是内部外部的变量依然不是同一个,他们的内存地址依然不同
    __block int a = 5;
    NSLog(@"%p",&a);
    void (^block)() = ^{
        a = 10;
        NSLog(@"%p",&a);
    };
    block();
    

    结果是:

    0x7fff5100ea88
    0x7fe931629858

    那么,加上__block就可以的本质原因就是传值方式的原因。

    1. 把未加__ block修饰的那段代码的ViewController.m的代码编译成C++代码,如图:
    未加__block修饰的.cpp代码文件

    画框代码即是核心部分,如下:

    
    int a = 5;
        void (*block)() = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, a));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    

    历尽艰难险阻终于找到a的影子,其中__ViewController__viewDidLoad_block_impl_0是一个结构体,在这里传了三个参数:(void *)__ViewController__viewDidLoad_block_func_0&__ViewController__viewDidLoad_block_desc_0_DATAa,不难看出,这里是直接将a作为参数传递,也就是值传递,既然是值传递,修改里面的值对外部的a自然是无效的。

    那么用__block修饰代码的cpp文件,就如下图:

    用__block修饰的.cpp代码文件

    画框部分是核心代码,如下:

     __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 5};
        void (*block)() = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    

    仅仅加了一个修饰词,代码就有了不小的变动,这里的结构体传了四个参数,a在第三个位置,并且a也有了修饰:(__Block_byref_a_0 *)&a,不难看出,这里的&标志着这里是指针传递,既然是指针传递,修改里面的值肯定会影响到外部的那个变量。

    相关文章

      网友评论

          本文标题:iOS - 初识 block

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