美文网首页iOS
block(一)

block(一)

作者: dandelionYD | 来源:发表于2019-03-21 10:01 被阅读12次

    面试题

    1.block的原理是怎样的?本质是什么?
    析:封装了函数的调用以及调用环境的OC对象,本质上也是一个oc对象,它内部也有个isa指针。
    2.__block的作用是啥?有什么使用注意点?
    解决block内部无法修改auto变量的问题。把__block修饰的变量封装成一个对象,放在block内部。
    3.block的属性修饰词为什么是copy?使用block有哪些使用注意点?
    析:block一旦没有进行copy操作,就不会在堆上,放在堆上是为了控制block的生命周期
       使用注意:循环引用问题
    4.block在修改NSMutableArray,需不需要添加__block?
     为数组增删改的时候不需要
     修改指针的对象的时候需要
    
    • 简单的使用

      #import <Foundation/Foundation.h>
      int main(int argc, const char * argv[]) {
          @autoreleasepool {
              int  age = 10;
              void (^myBlock)(int,int) =^(int a,int b){
                  NSLog(@"age=>%d",age);
                  NSLog(@"a=%d",a);
                  NSLog(@"b=%d",b);
              };
              age = 20;
              myBlock(20,30);
              NSLog(@"age=>%d",age);
          }
          return 0;
      }
      ---------------------------------------
      01.block的基本使用[25162:7452015] age=>10
      01.block的基本使用[25162:7452015] a=20
      01.block的基本使用[25162:7452015] b=30
      01.block的基本使用[25162:7452015] age=>20
      
    • block的本质

      使用clang编辑器转化为cpp文件:
      xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m  -o main.cpp
      

      通过分析:main.cpp我们发现

    block_01.jpeg block_02.png
    • Block的变量捕获(capture)

      为了保证block内部能够正常访问外部的变量,block有个变量捕获机制

    block_03.png
    #import <Foundation/Foundation.h>
    double height = 175;//全局变量(不会捕获的,而是传进去的是变量名,然后用到的时候是 直接访问的)
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            int age = 20;//auto变量的捕获 -->自动变量,离开作用域就销毁
            static NSString *name = @"Jack"; //static 此时传递的是变量的地址值
            
            void (^myBlock)(void) = ^(void){
                NSLog(@"block内部age =%d",age);
                NSLog(@"block内部name = %@",name);
                NSLog(@"block内部height = %f",height);
            };
            
            age = 30;
            NSLog(@"外age =%d",age);
            name = @"Lucy";
            height = 180;
            NSLog(@"外height = %f",height);
            myBlock();  
        }
        return 0;
    }
    
    打印:
    03.block变量捕获[89261:9561310] 外age =30
    03.block变量捕获[89261:9561310] 外height = 180.000000
    03.block变量捕获[89261:9561310] block内部age =20
    03.block变量捕获[89261:9561310] block内部name = Lucy
    03.block变量捕获[89261:9561310] block内部height = 180.000000
    

    分析:

    1.先将:main.m转化为cpp文件
    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m  -o main.cpp
    
    2.发现:__main_block_func_0
    
    /**************************************************/
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      int age = __cself->age; // bound by copy
      NSString **name = __cself->name; // bound by copy
    
                NSLog((NSString *)&__NSConstantStringImpl__var_folders_nn_5r5gqvmx1xz7008s3sn5kfb00000gn_T_main_b5d7dc_mi_1,age);
                NSLog((NSString *)&__NSConstantStringImpl__var_folders_nn_5r5gqvmx1xz7008s3sn5kfb00000gn_T_main_b5d7dc_mi_2,(*name));
                NSLog((NSString *)&__NSConstantStringImpl__var_folders_nn_5r5gqvmx1xz7008s3sn5kfb00000gn_T_main_b5d7dc_mi_3,height);
    }
    /**************************************************/
    我们发现:
    对于age(局部变量auto)、name(局部变量static):都是被捕获到了block里面了(使用了copy啦),并且age是值传递,对于name是指针传递
    【而】对于height(全局变量),block内部是直接访问的,传递进去的是变量名
    
    • block的类型(3种类型)

      • _ NSGlobalBlock_ (_ NSConcreteGlobalBlock _)
      • _ NSStackBlock _ (_ NSContreteStackBlock_)
      • _ NSMallocBlock _ (_ NSContreteMalloclock _ )

      我们来看看不同block的存放的内存区域

    block_04.png
    • 数据段中的__NSGlobalBlock__直到程序结束才会被回收
    • __NSStackBlock__类型的block存放在栈中,在栈中的内存由系统自动分配和释放,作用域执行完毕之后就会被立即释放
    • __NSMallocBlock__存放在堆中(需要我们自己进行内存管理)
    block_05.png

    体会

    分析:我们从block的本质来看block,本质上也是一个oc对象(里面也有isa)

    #import <Foundation/Foundation.h>
    int main(int argc, const char * argv[]) {
        @autoreleasepool { 
            void (^myBlock)(void) = ^(){
                NSLog(@"blcok内部");
            };
            NSLog(@"%@",[myBlock class]);
            NSLog(@"%@",[[myBlock class] superclass]);
            NSLog(@"%@",[[[myBlock class] superclass] superclass]);
            NSLog(@"%@",[[[[myBlock class] superclass] superclass] superclass]); 
        }
        return 0;
    }
    -----------------------------
    04.block的类型[89372:9603049] __NSGlobalBlock__
    04.block的类型[89372:9603049] __NSGlobalBlock
    04.block的类型[89372:9603049] NSBlock
    04.block的类型[89372:9603049] NSObject
    

    接下来:我们具体分析

    【在MRC环境下:】
    double height = 180;
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            void (^block1)(void) = ^{
                NSLog(@"Hello");
             };
            
            int age = 10;
            void (^block2)(void) = ^{
                NSLog(@"Hello - %d", age);
            };
            
            NSLog(@"%@ %@ %@", [block1 class], [block2 class], [^{
                NSLog(@"%d", age);
            } class]);
            
            NSLog(@"%@",[[^{
                NSLog(@"%d", age);
            } copy] class]);
            
            NSLog(@"%@",[^{
                NSLog(@"%f", height);
            } class]);
            
            NSLog(@"%@",[[^{
                NSLog(@"%f", height);
            }copy] class]);
        }
        return 0;
    }
    
    结果:
    04.block的类型[9877:10384412] __NSGlobalBlock__ __NSStackBlock__ __NSStackBlock__
    04.block的类型[9877:10384412] __NSMallocBlock__
    04.block的类型[9877:10384412] __NSGlobalBlock__
    04.block的类型[9877:10384412] __NSGlobalBlock__
    
    分析:
    1.没有访问auto变量的block是__NSGlobalBlock__类型的,存放在数据段中
    2.访问了auto变量的block是__NSStackBlock__类型的,存放在栈中
    3.__NSStackBlock__类型的block调用copy成为__NSMallocBlock__类型并被复制存放在堆中
    
    

    对于:NSStackBlock类型的block存放在栈上的
    下面我们在来看看一个有趣具体的例子

    同样是在【MR】的环境下----
    void (^block)(void);//定义一个block变量
    void test(){
      int a = 10;
      block = ^{
          NSLog(@"block---------%d", a);
      };
    }
    int main(int argc, const char * argv[]) {
      @autoreleasepool {
          test();
          for (int i=0; i<10; i++) {
              NSLog(@"%d",i);
          }
          block();//发现程序在这里崩溃了 【坏内存访问】
      }
      return 0;
    }
    
    ----------------------
    void (^block)(void);//定义一个block变量
    void test(){
      int a = 10;
      block = [^{
          NSLog(@"block---------%d", a);
      } copy];//使用了copy
    }
    int main(int argc, const char * argv[]) {
      @autoreleasepool {
          test();
          for (int i=0; i<10; i++) {
              NSLog(@"%d",i);
          }
          block();
          NSLog(@"%@",[block class]);//__NSMallocBlock__
      }
      return 0;
    }
    05.blcok的类型2[24991:10706450] 0
    05.blcok的类型2[24991:10706450] 1
    05.blcok的类型2[24991:10706450] 2
    05.blcok的类型2[24991:10706450] 3
    05.blcok的类型2[24991:10706450] 4
    05.blcok的类型2[24991:10706450] 5
    05.blcok的类型2[24991:10706450] 6
    05.blcok的类型2[24991:10706450] 7
    05.blcok的类型2[24991:10706450] 8
    05.blcok的类型2[24991:10706450] 9
    05.blcok的类型2[24991:10706450] block---------10
    05.blcok的类型2[25002:10708430] __NSMallocBlock__
    
    发现:成功打印了
    分析:
    在未使用copy的时候,block变量的作用域是test函数的右边的 "}",所以就会出现坏内存访问
    而使用了copy之后,有效的延长了block变量的作用域,从最后的打印结果来看,确实是变成了【堆】blcok
    

    那么其他2个类型的block进行了copy操作之后呢?

    【MRC环境下】
    void (^globalBlock)(void);
    void (^mallocBlock)(void);
    void test(){
        globalBlock = [^{
            NSLog(@"没有访问auto变量");
        } copy];// __NSStackBlock__ 调用copy 转化为__NSMallocBlock__
        
        int a = 10;
        mallocBlock =[[^{
            NSLog(@"a=%d",a);
        } copy] copy]; // __NSMallocBlock__ 调用copy
    }
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            test();
            for (int i=0; i<10; i++) {
                NSLog(@"i=%d",i);
            }
            globalBlock();
            mallocBlock();
            NSLog(@"%@",[globalBlock class]);
            NSLog(@"%@",[mallocBlock class]); 
        }
        return 0;
    }
    
    打印:
    06.block类型copy的作用[25170:10716981] I=0
    06.block类型copy的作用[25170:10716981] I=1
    06.block类型copy的作用[25170:10716981] I=2
    06.block类型copy的作用[25170:10716981] I=3
    06.block类型copy的作用[25170:10716981] I=4
    06.block类型copy的作用[25170:10716981] I=5
    06.block类型copy的作用[25170:10716981] I=6
    06.block类型copy的作用[25170:10716981] I=7
    06.block类型copy的作用[25170:10716981] I=8
    06.block类型copy的作用[25170:10716981] I=9
    06.block类型copy的作用[25170:10716981] 没有访问auto变量
    06.block类型copy的作用[25170:10716981] a=10
    06.block类型copy的作用[25170:10716981] __NSGlobalBlock__
    06.block类型copy的作用[25170:10716981] __NSMallocBlock__
    

    至此我们可以得出:

    block_06.jpg

    补充:数据存储位置

    #import <Foundation/Foundation.h>
    
    @interface myObject : NSObject
    @end
    @implementation myObject
    @end
    
    int age = 10;
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            int a = 10;
            NSLog(@"数据段:age %p", &age);
            NSLog(@"栈:a %p", &a);
            NSLog(@"堆:obj %p", [[NSObject alloc] init]);
            NSLog(@"数据段:class %p", [myObject class]);
        }
        return 0;
    }
    

    友情链接:

    相关文章

      网友评论

        本文标题:block(一)

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