zoukankan      html  css  js  c++  java
  • Block使用的简单总结

    【Block的简单使用】
      1. block 当作参数来传递
      如下定义一个没有返回值无参数的 block ,并把它作为参数,让系统调用,注意:这里是系统在调用,因为 UIView 动画结束是系统调用的。
        void(^myBlock)(void) = ^() {
            NSLog(@"定义了一个 Block");
        };
        [UIView animateWithDuration:0.3 animations:myBlock];

      那么为什么需要把 block 当作参数去使用呢?

      这就引出了 block 这个时候的使用场景:当自己封装一个类的时候,有些事情由外部决定,但什么时候做由内部决定,(即内部决定执行时间,外部传入具体做些什么)——这个时候就可以使用 block 来作为参数。

      2. block 当作返回值来使用
      如下在控制器写下面代码, test 为方法名, void(^)(void) 就是 block 的类型,它就是方法的返回值类型。
    - (void(^)(void))test {
        return ^{
            NSLog(@"test");
        };
    }

      那么上面这个方法该如何调用呢?

      第一种常规方式: [self test](); ,因为 test 是一个方法,所以 [self test] 调用方法后得到的是一个 block,而执行 block 只要在它后面加  () 就可以。所以 [self test](); 打印出了 test。

      第二种方式: self.test(); 这样类似于属性的 get 方法,后面加一个  () 就相当于执行 block。这里可以留存一个疑问:oc 的 get 方法其实本质是 Xcode 的编译器特性,使用点语法可以对应到属性的 get 方法。那为什么方法返回值为 block 时使用点语法就能被调用到了呢?(待深究)

      block 链式编程
      把方法调用通过语法链接起来,可读性非常好。如下代码,AddTool 继承自 NSObject,用它来执行链式累加。
      .h 文件
    #import <Foundation/Foundation.h>
    
    @interface AddTool : NSObject
    
    - (AddTool *(^)(NSInteger))add;
    
    @property (nonatomic, assign) NSInteger result;
    
    @end

      .m 文件

    #import "AddTool.h"
    
    @implementation AddTool
    
    - (AddTool *(^)(NSInteger))add
    {
        return ^(NSInteger value) {
            _result += value;
            return self;
        };
    }
    
    @end

      链式调用如下:

        AddTool *addTool = [AddTool new];
        NSInteger result = addTool.add(5).add(3).add(1).result; // NSLog: 9

    【Block 逆传值】

      这里用控制器跳转举例 block 的逆传值。A push 到 B 之后,B 传值到 A 中。可以在 B 头文件声明一个 block 属性,在 .m 文件中执行这个 block。

      B  .h 文件:

    @interface BViewController : UIViewController
    
    @property (nonatomic, strong) void(^callBack)(NSString *str);
    
    @end

      B .m 文件

    - (void)viewDidLoad {
        [super viewDidLoad];
        self.callBack(@"123");
    }

      在 A 中 push 处的代码就可以收到 B 传过来的值。

        BViewController *bVC = [[BViewController alloc] init];
        bVC.callBack = ^(NSString *str) {
            NSLog(@"%@", str); // NSLog: 123
        };
        [self.navigationController pushViewController:bVC animated:YES];

    【Block的循环引用】

      循环引用的描述:Block的拥有者在Block作用域内部又引用了自己,因此导致了Block的拥有者永远无法释放内存,就出现了循环引用的内存泄漏。

      发生的场合:ARC 中 block 为 strong 或 copy 属性,在Block内部使用了当前类的self属性,同时这个类包含了别一个类的 Block 属性。

    举例:还是上面的传值的例子,现假如在A的.m文件的block里使用了self.view.backgroundColor = [UIColor redColor] 现在这些对象之间的引用关系如下图所示:
    因此这样就就造成了循环引用。
     
    Block 内循环引用的解决:
    在如下的例子,可以在 Block 外加上这样一句(如果将 typeof 用于表达式,则该表达式不会执行。只会得到该表达式的类型。)
     __weak typeof(self) weakSelf = self; 或  __weak AViewController *weakSelf = self; 
     
    【Block 内存分析】
      1. 内存5大区 —— 堆, 栈, 方法区,静态区(全局区),常量区
    • 全局区和静态区: 它们其实是一样的,从内存上来看,全局变量和静态变量都是保存在静态存储区,生命期和程序一样,都是在静态数据区分配内存。在程序结束后回收内存。
       它们之间作用域有所不同,全局变量的作用域是整个项目,静态全局变量是当前程序文件,静态局部变量是当前函数体内。
    • : 手动管理内存    
    • 栈: 代码块一过,系统自动回收对应内存区
     首先来了解一下 Block,苹果官方文档 Block 是对象。(因为它是对象,所以用%@格式打印可以看到它的内存分配区),文档描述第一句话如下:
     

      2. MRC 下 Blcok 内存分配

        在 ARC 之前,Block 的内存管理需要区分是 Global (全局)、Stack (栈) 还是 Heap (堆)。

    • 全局区: 如下定义一个Block,并将它使用, 它的打印结果为 <__NSGobalBlock__: 0x1075c076> ,global 表示全局区。全局区表示到处都可以使用。(ps:假如block内部访问static修辞的外部局变量,那么它也是在全局区,关于静态区与全局刚刚上面已经解释过)  
        void(^block)() = ^{
            
        };
        NSLog(@"%@", block);
    • 栈区:如下当Block内访问一个外面的局部变量a,它的打印结果为 <__NSStockBlock__: 0x7fff5baa09d8> ,stack表示栈区
        int a = 0;
        // block 存放在全局区
        void(^block)() = ^{
            NSLog(@"%d", a);
        };
    总结如下:Block的内存分配与它内部访问的变量有关,如果访问的是全局变量,那Block会在全局区;  如果访问局部变量,那 Block 会分配到栈区。(什么也不访问默认在全局区)
    • 堆区:使用 copy 进行强引用时 block 会 copy 一份到堆区

      在 MRC 环境下用 retain 修辞 Block 会有黄色警告,如下图,警告提示最好使用 copy

     
    假如现在就用 retain,并且在 block 内访问了一个局部变量 a (这个 a 就写在 Block 的上面),代码如下,这时发现一运行程序就漰了。(坏内存访问,报错会出现在使用 self.Block 的地方)
        int a = 0;
        void(^block)() = ^{
            NSLog(@"调用block");
            NSLog(@"%d", a);
        };
        
        self.block = block; // 报错
    这一点总结:在非ARC下不能用 retain 引用 block,因为这样不会把 block 放在堆里,它会在栈区,所以代码区一出括号就会销毁,于是会报错。只要使用 copy 就可以把 block 放到堆里面。这样block就不会销毁。——所以说 block 要用 copy 都是老程序员说的,因为那时没有ARC.
     
      3. ARC 下 Block 内存分配
      ARC之后,苹果自动会将所有原本应该放在栈中的Block全部放到堆中。

      首先,全局的 Block 比较简单,一句话就可以讲完:凡是没有引用到 Block 作用域外面的参数的 Block 都会放到全局内存块中,在全局内存块的 Block 不用考虑内存管理问题。(放在全局内存块是为了在之后再次调用该 Block 时能快速反应,当然没有调用外部参数的 Block 根本不会出现内存管理问题)。

       所以 Block 的内存管理出现问题的,绝大部分都是在堆内存中的 Block 出现了问题。实际上属于 Block 特有的内存管理问题就只有一个:循环引用。

    • 全局区:还是如下代码,默认 Block 还是放在全局区,没访问外部变量就都是在全局区,与是否ARC无关
        void(^block)() = ^{
            
        };
        NSLog(@"%@", block);
    • 堆区: 访问一个外部局部变量或对象时,代码如下,它的打印结果 <__NSMallocBlock__: 0x7f9f5baa09d8> ,这个 malloc 分配的内存是表示在堆上。(这有点像ARC下默认一个对象就是强引用,好处是不会一创建就销毁。)
        int a = 0;
        void(^block)() = ^{
            NSLog(@"%d", a);
        };
        NSLog(@"%@", block);
     【Block 内变量值传递与指针传递】
      看下面代码,这个很简单,block 打印出来肯定是 10
        int a = 5;
        a = 10;
        void(^block)() = ^{
            NSLog(@"%d", a); // NSLog: 10
        };
        block();

      1. 值传递情况

      再看如下代码,这个打印出来是5,虽然 block() 执行之前 a = 10,但因为 a = 5 之后就立即被传递进了 block,只是还没有执行而已,即内存已配好。而且这时的 block 是值传递,这时外面更改无法改变里面的值。(如果是指针传递则外面可以更改里面的值)

        int a = 5;
        // 值传递
        void(^block)() = ^{
            NSLog(@"%d", a); // NSLog: 5
        };
        a = 10;
        block();
      
      2. 指针传递情况 
      那么再看如下代码,这个打印结果会是10。原因:只要局部变量生命周期是整个 app 运行都在,那么就是指针传递这里加 static 后,生命周期就与整个程序同存亡了。与全局区一样(这一点上面 Block 内存分析时分析过了)
        static int a = 5;
        // 指针传递
        void(^block)() = ^{
            NSLog(@"%d", a); // NSLog: 10
        };
        a = 10;
        block();

      下在情况也是指针传递,所以打印出来也会是10

        __block int a = 5;
        // 指针传递
        void(^block)() = ^{
            NSLog(@"%d", a); // NSLog: 10
        };
        a = 10;
        block();

      block变量传递总结:如果在block内部访问的是局部变量,那么就是值传递,否则就是指针传递。

  • 相关阅读:
    ubuntu下安装常用软件合集
    Ubuntu16升级到18
    VScode安装教程
    查看系统信息脚本
    Excel应用笔记
    后缀数组
    笔记-AHOI2013 差异
    二分图
    动态规划dp
    笔记-CF1354E Graph Coloring
  • 原文地址:https://www.cnblogs.com/buerjj/p/5497706.html
Copyright © 2011-2022 走看看