iOS多线程_03_Block

  1、block定义

  是准备好的一段代码片段,在需要的时候执行

  注意:block 是C语言的格式

  2、block格式

  格式:返回类型 (^blockName) (参数类型) = ^(形参列表) { 代码实现 };

  提示:如果没有参数,格式可以简化

  格式:返回类型 (^blockName) () = ^ { 代码实现 };

  在Xcode代码编辑中输入 inlineBlock,有提示,这是block格式的快捷方式

  3、示例代码

  (1)先看看block怎么用

    void (^myBlock)() = ^ {
        NSLog(@"hello block");
    };
    
    // 调用block
    myBlock();

  输出结果是:hello block

  (2)带参数

    void (^sumBlock)(int, int) = ^(int x, int y) {
        NSLog(@"%d", x + y);
    };
    
    sumBlock(10, 20);

  输出结果是:30

  (3)block到底干了什么

    int x = 10;
    NSLog(@"%p", &x);
    
    // 在定义block时,如果block中使用到外部的“局部”变量,block会建立该变量的副本(会记录当前x的数值)
    void(^myBlock)() = ^ {
        NSLog(@"%d", x);
        NSLog(@"%p", &x);
    };
    myBlock();

  输出结果:

  0xbfffc8e4

  10

  0x8e632a4

  从地址可以看出block内部的x跟外部的x不是同一个,不过内容都是10

  block的作用:在 NSLog(@"%d", x); 中,因为使用到“局部变量”x,所以myBlock会建立x的副本,在输出 NSLog(@"%d", x);的时候把x的值输出。

  所以,可以得出结论,block里面用到了谁,block就会建立谁的副本,在使用的时候输出它的值。

  4、常见面试题

  (1)在使用外部变量时,默认不允许在块代码中直接修改外部变量的数值

  上边这张图很明显的说明,默认情况下是不允许在块代码中直接修改外部变量的数值的

  (2)如果要修改外部变量的值,需要在定义变量的时候用 __block 修饰

  (3)__block的另一个特点:被__block修饰的变量,在值改变的时候,块代码内会同步被改变后的值

  上面这段代码的输出结果是:

  0xbfffc8e8

  20

  === 0x8cadbb0

  20

  结果表明,在 x = 20; 之后,block内部的x值的副本也改变了

  如果把 __block 去掉,输出结果是:

  0xbfffc8e4

  10

  === 0xa275f74

  20

  注意:在x = 20;之后调用block才会同步20的值,一定不要忘了代码是顺序执行的

  提示:为了保证代码的可读性,通常不使用 __block

  总结:上面介绍的block的用法,block本质上是创建了副本,要注意的是,副本的内容还是变量的内容,而副本在内存中存储的地址变了

  (4)再来看block保存指针变量

 1     // 指针包含指针的地址,和指向的地址
 2     NSMutableString *str = [NSMutableString stringWithString:@"hello"];
 3     NSLog(@"%p %p", &str, str);
 4     
 5     void(^myBlock)() = ^ {
 6         // 修改指针指向内存空间的内容
 7         [str setString:@"world"];
 8         NSLog(@"%p %p", &str, str);
 9         NSLog(@"%@", str);
10     };
11     
12     myBlock();
13     NSLog(@"%p %p", &str, str);
14     NSLog(@"%@", str);

  输出结果是:

  0xbfffc8e4 0x8c281f0

  0x8d0b834 0x8c281f0

  world

  0xbfffc8e4 0x8c281f0

  world

这幅图并不准确,block保存的副本具体在哪儿存着,我也不是太清楚,因为从地址观察,block副本很可能不是在栈中,不过,有一点确定的是,block肯定建立了一个副本。可以将block当做特殊的对象。

  第3行代码,分别输出了栈中指针str的地址和堆中对象hello的地址。

  第5行代码,block建立了str的副本指针,内容不变,地址变了;然后第7行代码修改了block中str指向的对象的内容,因此堆中对象的内容变为world。

  第13行代码,可以说明,指针str的地址和指向的地址都没有变。

  第14行代码,输出了str指针指向的对象内容。

原文地址:https://www.cnblogs.com/yudigege/p/3930408.html