iOS

block(一)

2019-03-21  本文已影响12人  dandelionYD

面试题

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

友情链接:

上一篇 下一篇

猜你喜欢

热点阅读