• block 专题--基础


    #######以前只是看博客园,这么长时间了,也要有点干货分享给大家,不能只索不予,希望对大家有点帮助########

    一.block的基本概念

    /**
     1. block的概念和特点:
         1> block是 C语言中的一种数据类型
         2> block是一个提前准备好的能工作的代码块,可以在任何需要的时候被执行。
         3> 本质上是轻量级的匿名函数,可以作为其他函数的参数或返回值。
         4> block本身可能有一个参数列表,也可能有一个返回值。
         5> 可以将block赋给一个变量,并在需要的时候调用,就像调用一个普通函数一样。
     */

    二.定义block

    1、无参无返回值的block

    (1) 格式:
         void (^block的名称)() = ^ { 代码实现; };
    (2) 定义block    
           // 定义block
            void(^myBlock)() = ^ {
                NSLog(@"hello");
            };
           // 定义函数
            void demo() {
            }
    (3) 定义block有以下特点:
    1. 类型比函数多了一个^ 和 小括号。
    2. 设置数值,需要有一个等号, 等号右边有一个 ^, 内容是{}括起来的一段代码。
    3. 如果block没有参数,等号右边的小括号可以省略。
    4. block定义完成之后,只是准备好的一个代码块,不能自己执行,这一点跟函数类似,函数必须被调用才会执行。
    (4) 注意
            定义block的时候,把它当成数据类型。
            执行block的时候,把它当成函数。

    2、带参无返回值的block

    (1) 格式
         void (^block的名称)(参数类型) = ^(参数列表) { 代码实现; };  
    (2) 定义block
       // 定义带参数的block
       void (^sumBlock)(int, int) = ^(int x, int y) {
           NSLog(@"%d", x + y);
       };
       // 执行block
       sumBlock(1, 5);
     
    注意: 等号右边必须设置参数的名称。
     
    3、带参带返回值的block
    (1) 格式
    返回值类型 (^block的名称)(参数类型) = ^返回值类型(参数列表) { 代码实现; };
    (2) 定义block
         int (^sumBlock2)(int, int) = ^ int (int a, int b) {
                return a + b;
         };
         int sum = sumBlock2(10, 20);
    NSLog(@"%d", sum);

    4、block定义的速记符号

    速记符号:inlineBlock 能够快速敲出一个block的基本结构
    returnType (^blockName)(parameterTypes) = ^(parameters) {
                statements;
    };
    注意:可以利用inlineBlock辅助记忆,但是不要依赖速记符号,必须能够手写定义block。

    三.block常见面试题

    1、第一道:block引用外部变量

    void blockDemo1() {
        int x = 10;
        void (^myBlock)() = ^ {
            NSLog(@"%d", x);
        };
       
        x = 20;
       
        myBlock();
    }
    (1) 提问: 输出是多少?
    (2) 答案: 输出是10.
    (3) 原因: 定义block的时候,如果引用了外部变量,会对外部变量做一个copy,记录住定义block时变量的数值。
    (4) 如果后续在block外部修改x的值,不会影响block内部的数值变化!
    为什么?
    因为我们定义的变量x是保存在栈区的,而block引用外部变量,对其做copy操作,会将外部变量copy到堆区。这样的话,在block外部修改的只是栈区的x,当然不会影响到block内部的x。
    (5) 验证:
    void blockDemo1() {
        int x = 10;
        NSLog(@"定义前: %p", &x);              // 栈区
       
        void (^myBlock)() = ^ {
            NSLog(@"%d", x);
            NSLog(@"in block: %p", &x);       // 堆中的地址
        };
       
        NSLog(@"定义后: %p", &x);              // 栈区
        x = 20;
       
        myBlock();
    }
     
    执行结果如下:
     

    2、第二道:block内部修改外部变量的值

    仍然是第一道题,不过如果就想在block内部修改x,该怎么做?
    void blockDemo2() {
        int x = 10;
        NSLog(@"定义前: %p", &x);             
       
    void (^myBlock)() = ^ {
       x = 80;
            NSLog(@"%d", x);
            NSLog(@"in block: %p", &x);      
        };
       
        NSLog(@"定义后: %p", &x);             
        x = 20;
       
        myBlock();
    }
    (1) 提问: 上面的代码有没有问题?若有问题,该怎么更正?更正后的输出结果是多少?
    (2) 答案:
    /**
     (1) 如果在block内部直接修改x,会报错。因为默认情况下,不允许block内部修改外部变量的值。
     (2) 如果想在block内部修改外部变量的值,需要使用__block修饰外部变量。
     */
    (3) 更正:
     
    void blockDemo2() {
        // 使用 __block 说明不再关心x数值的变化
        // 定义block的时候,如果引用了外部使用__block修饰的变量,block定义之后,外部变量的地址同样会变成堆中的地址
        __block int x = 10;                   // 在类型前面添加 __block
        NSLog(@"定义前: %p", &x);              // 栈区
       
        void (^myBlock)() = ^ {
            x = 80;
            NSLog(@"%d", x);
            NSLog(@"in block: %p", &x);       // 堆中的地址
        };
       
        NSLog(@"定义后: %p", &x);              // 堆中的地址
        x = 20;
       
        myBlock();
    }
    3> 输出结果是 80。
    执行结果如下:   
     
    注意:
    (1) 使用 __block 说明不再关心x数值的变化
    (2) 定义block的时候,如果引用了外部使用__block修饰的变量,block定义之后,外部变量的地址同样会变成堆中的地址
    也就是说:只要使用__block修饰了变量x,那么在block定义完成之后,再去修改x,修改的都是堆区的x。

    3、第三道

    void blockDemo3() {
        // 指针记录的是地址
        NSMutableString *strM = [NSMutableString stringWithString:@"zhangsan"];
     
        void (^myBlock)() = ^ {
            [strM setString:@"lisi"];
        };
        myBlock();
        NSLog(@"%@", strM);
    }
    1. 提问: 以上代码有没有问题?
    2. 答案: 没有问题
    3. 分析: 如下 
    void blockDemo3() {
        // 指针记录的是地址
        NSMutableString *strM = [NSMutableString stringWithString:@"zhangsan"];
        NSLog(@"定义前: %p %p", strM, &strM);
       
        void (^myBlock)() = ^ {
            [strM setString:@"lisi"];
            NSLog(@"in block: %p %p", strM, &strM);
    //        strM = [NSMutableString stringWithString:@"lisi"];
        };
       
        NSLog(@"定义后: %p %p", strM, &strM);
       
        myBlock();
        NSLog(@"%@", strM);
    }
    执行结果如下:
     
    1>.首选要明确以下关系:
     
     
     
     
    strM 本质是一个指针变量,表示的是字符串对象的地址 0x1001038d0
    &strM 表示的是指针strM在栈区的地址
    block内部默认不能修改外部变量的值。在这道题中,外部变量是strM,它的值是0x1001038d0。
    当我们在block内部使用 [strM setString:@”lisi”];这句代码,首先会将strM拷贝到堆区,这个时候strM的值并没有改变,改变是&strM(即strM的地址,因为strM由栈区来到了堆区)然后做 setString的操作,只是在修改strM指向的字符串对象的值,也没有修改strM的值。所以,如果block内部是[strM setString:@”lisi”];程序是没有问题的。
            但是,如果block内部是: strM = [NSMutableString stringWithString:@”lisi”];相当于重新创建了一个字符串对象,让strM指向它,这样的话,很显然strM的指向发生了改变,那么它保存的地址也就发生了改变,即外部变量的值被改变了。因此会报错。
     
     
    Block的内存管理:
     
     
     
     

     
    循环遍历   
       我们常用的循环遍历有三种:  
    for循环
    for-in循环
     
     
    block遍历循环:
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    // 先有一个数组
        NSArray *array = @[@"1", @"2", @"3", @"4", @"5",];
       
        // enumerateObjectsUsingBlock: 是 NSArray 的方法  :用于block的枚举对象
        // obj: 数组元素
        // idx: 数组下标
        // *stop: 是否停止遍历
       
        [array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            NSLog(@"%@", obj);
           
            if (idx == 2) {
                *stop = YES;
            }
        }];
    }
     
    控制台输出结果:
    2016-01-29 18:44:47.312 block 遍历[5011:700719] 1
    2016-01-29 18:44:47.312 block
    遍历[5011:700719] 2
    2016-01-29 18:44:47.312 block 遍历[5011:700719] 3 
  • 相关阅读:
    正则表达式
    scrollTop
    css3
    错误整理
    jquery-2
    vscode_修改字体,使用Fira Code
    实例_一个循环嵌套函数
    js_getComputed方法和style属性关于读取样式的区别
    html_html5增强的文件上传域_使用FileReader读取文件内容
    html_html5增强的文件上传域_FileList对象与File对象
  • 原文地址:https://www.cnblogs.com/luckhao/p/5453173.html
Copyright © 2020-2023  润新知