美文网首页
iOS block之三种block

iOS block之三种block

作者: luonaerduo | 来源:发表于2019-08-05 14:52 被阅读0次

    前言
    先说明一下,因为ARC下系统会对block做一些拷贝和释放操作,对深入于理解block无益,所以本篇文章所提到的栗子编译环境均为MRC。

    首先引用《Objective-C高级编程》Blocks章节中的第一句话:Blocks是对C语言的扩充功能。而且OC是建立在C语言基础上之上,添加了面向对象机制的一门编程语言。

    所以不要再说block的实现原理是C++的函数指针了,正确答案是:block的实现原理是C语言的函数指针。
    函数指针即函数在内存中的地址,通过这个地址可以达到调用函数的目的。

    Block是NSObject的子类,拥有NSObject的所有属性,所以block对象也有自己的生命周期,生存期间也会被持有和释放。

    block有三种:
    NSGlobalBlock 静态区(全局区)block,这是一种特殊的bloclk,因为不引用外部变量而存在。另外,作为静态区的对象,它的释放是有操作系统控制的,这一点我们最后再聊。
    NSStackBlock 栈区block,位于内存的栈区,一般作为函数的参数出现。
    NSMallocBlock 堆区block,位于内存的堆区,一般作为对象的property出现。

    如果一个blcok引用了外部变量是栈block,则其不引用外部变量就成为了静态blcok。
    如果一个block引用了外部变量是堆block,则其不引用外部变量就成为了静态block。

    NSStackBlock 栈区block
    说到栈操作,大家都明白,出栈和入栈,函数只有入栈后才能执行,出栈后就释放了。
    栈block一般在函数内部定义,并在函数内部调用;或者在函数外部定义,作为函数的一个参数在函数内部调用。函数出栈时和其他变量或参数一起释放。

    栈区block形式有2,如下:

    栈区block形式1

    • (void)xxx
      {
      __block int i = 0;
      void (^block1)() = ^{
      //此处若不引用外部变量i,则block1是静态block,若引用,则为栈block
      i++;
      NSLog(@"i = %d",i);
      };

      block1();
      }
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      栈区block形式2:

    • (void)saveFile:(NSDictionary *)dic complete:(void(^)(BOOL success))complete
      {
      //dic写入本地近作demo使用,实际开发要判断路径是否存在,以及dic中是否存在null值等
      NSString * path = [NSHomeDirectory() stringByAppendingFormat:@"/Caches/dicInfo"];
      BOOL suc = [dic writeToFile:path atomically:YES];

      if (complete) {
      //若complete中不引用外部变量suc,则complete是静态block,此处complete为栈block
      complete (suc);
      }
      }
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      NSMallocBlock 堆区block
      堆区是内存的常驻区域,也叫永久存储区,block一般在函数中定义,最多是个栈block,什么时候才能打怪升级成为堆block呢?
      在MRC时代你需要使用Block_copy()方法,才可以将blcok复制到堆中。

    然而复制到堆中有何用处呢?
    首先,作为一个对象,把它复制到堆中,想要使用它肯定要有一个指针指向它,而指向它的指针是作为property或静态变量出现的(如果不被引用也就没有了常驻于堆区的意义),而实际开发中多使用property引用。

    在MRC中,如果一个类的block属性是使用copy修饰的,则不需要手动调用Block_copy将其复制到堆中。如果是用strong修饰的,则必须使用Block_copy()将其复制到堆中,并在释放时调用Block_release()方法将其释放,否则会因野指针导致程序崩溃。

    @interface TestV : UIView

    @property (nonatomic, strong) void(^block1)();
    @property (nonatomic, copy) void(^block2)();

    @end
    1
    2
    3
    4
    5
    6

    • (void)testFunc
      {
      __block int i = 0;

      void (^block1)() = ^{
      //此处若不引用外部变量i,则block1是静态block,若引用,则为堆block
      i++;
      NSLog(@"block1 -- i = %d",i);
      };
      void (^block2)() = ^{
      i --;
      NSLog(@"block2 -- i = %d",i);
      };

      TestV * view = [[TestV alloc] initWithFrame:CGRectMake(100, 100, kScreenWidth - 200, 40)];
      [self.view addSubview:view];

      view.block1 = Block_copy(block1);
      view.block2 = block2;

      NSLog(@"block1 -- %@",block1);
      NSLog(@"block2 -- %@",block2);
      }
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      因此在MRC中,基本上会用copy关键字修饰block,既优雅又省代码,还避免了因为未调用Block_release而造成内存泄露,这大概是很多人在ARC项目中依旧使用copy修饰block的历史原因。

    其实在ARC下使用strong或copy修饰block没啥区别,亲测有效,可能会遇到一些开发人员,他们看到ARC工程中出现strong修饰的block就会提出质疑,那只能说明他们没有亲自写两行代码测试过是否真的有区别。

    NSGlobalBlock 静态block
    既然存在于静态区,则只有当进程被杀死,进程所占用的内存空间被释放后,静态区的对象才会被释放。静态block并不依附于某个对象而存在,也并不为某个类对象单独享有,而是所有的该类对象都共同拥有这个block,都有这个block的使用权,不管有多少个类对象,在程序运行期间这个block始终只有一份,直到进程被杀死,内存被释放。

    以下代码片段将有助于你的理解:

    //在一个成员方法中定义一个静态block,然后打印出self的地址和block的地址

    • (void)blockAction
      {
      void(^block1)() = ^{
      //未引用外部变量,则为静态block
      NSLog(@"block1");
      };

      NSLog(@" -- self : %p",self);
      NSLog(@" -- %@ --",block1);
      }
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      //初始化了两个对象,同时调用各自的成员方法
      [_v1 blockAction];
      [_v2 blockAction];
      1
      2
      3
      //打印结果如下:两个对象地址不同,两个block地址相同
      xxx BlockDemo[22106:1969807] -- self : 0x7f81da513f20
      xxx BlockDemo[22106:1969807] -- <NSGlobalBlock: 0x1057ca1a0> --
      xxx BlockDemo[22106:1969807] -- self : 0x7f81da512770
      xxx BlockDemo[22106:1969807] -- <NSGlobalBlock: 0x1057ca1a0> --


    作者:future
    来源:CSDN
    原文:https://blog.csdn.net/Mo_Mo123/article/details/77923634
    版权声明:本文为博主原创文章,转载请附上博文链接!

    相关文章

      网友评论

          本文标题:iOS block之三种block

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