block 的演练和使用

概念

  • block 是 C 语言的
  • 是一种数据类型,可以当作参数传递
  • 是一组预先准备好的代码,在需要的时候执行
  • 动画 block 回顾

    self.demoView.center = CGPointMake(self.view.center.x, 0);
    // 此方法会立即执行动画 block
    [UIView animateWithDuration:2.0 delay:0 usingSpringWithDamping:0.3 initialSpringVelocity:10 options:0 animations:^{
        NSLog(@"动画开始");
        self.demoView.center = self.view.center;
    } completion:^(BOOL finished) {
        // 会在动画结束后执行
        NSLog(@"动画完成");
    }];
    NSLog(@"come here");

    block 基本演练

    • 最简单的 block
      - (void)blockDemo1 {
      
          // 定义block
          // 类型 变量名 = 值
          void (^block)() = ^ {
              NSLog(@"Hello block");
          };
      
          // 执行
          block();
      }

      使用 inlineBlock 可以快速定义 block,不过 block 一定要过关

    • 当作参数传递
      - (void)blockDemo2 {
          void (^block)() = ^ {
              NSLog(@"Hello block");
          };
      
          [self demoBlock:block];
      }
      
      ///  演示 block 当作参数传递
      - (void)demoBlock:(void (^)())completion {
          NSLog(@"干点什么");
      
          completion();
      }
    • 使用局部变量
      - (void)blockDemo3 {
          // 栈区变量
          int i = 10;
          NSLog(@"%p", &i);
      
          void (^block)() = ^ {
              // 定义 block 的时候会对栈区变量进行一次 copy
              NSLog(@"Hello block %d %p", i, &i);
          };
      
          [self demoBlock:block];
      }

      如果 block 中使用了外部变量,会对外部变量做一次 copy

    • 在 block 中修改外部变量
      - (void)blockDemo4 {
          // 栈区变量
          __block int i = 10;
          NSLog(@"%p", &i);
      
          void (^block)() = ^ {
              // 定义 block 的时候会对栈区变量进行一次 copy
              NSLog(@"Hello block %d %p", i, &i);
              i = 20;
          };
      
          NSLog(@"block 定义完成 %p %d", &i, i);
      
          [self demoBlock:block];
      
          NSLog(@"===>%d", i);
      }

      如果要在 block 内部修改栈区变量,需要使用 __block 修饰符,并且定义 block 之后,栈区变量的地址会变化为堆区地址block 的内存位置

      block 的内存位置

    •      全局区:如果block中没有使用任何全局变量
      • 栈区:如果 block 中使用了外部变量
        • MRC 模式可以看到
        • ARC 模式,系统会自动将 Block 复制到堆中
      • 堆区:将 block 设置给 copy 属性
        @property (nonatomic, copy) void (^myBlock)();
        - (void)blockDemo5 {
            int i = 10;
            void (^block)() = ^ {
                NSLog(@"i --- %d", i);
            };
        
            NSLog(@"%@", block);
        
            self.myBlock = block;
        }
        
        - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
        
            NSLog(@"%@", self.myBlock);
        }

        注意:虽然目前 ARC 编译器在设置属性时,已经替程序员复制了 block,但是定义 block时,仍然建议使用 copy 属性

        // 面试题
        - (void)blockDemo4 {
            int i = 10;
            
            // 提问:输出是几?
            // 10,定义 block 的时候,10已经被复制到堆中了
            void (^block)() = ^ {
                NSLog(@"%d", i);
            };
            
            i = 20;
            
            block();
        }

        问:block在哪里?=> 全局区

        - (void)blockDemo5 {
            // 问:block在哪里?=> 全局区
            // 原因:block 中没有使用任何的外部变量,不会因为任何外部因素而改变
            // 会保存在全局区
            void (^block)() = ^ {
                NSLog(@"hello world");
            };
            NSLog(@"%@", block);
        }

        问:block在哪里?=> 堆区

        - (void)blockDemo6 {
            int i = 10;
            // 问:block在哪里?=> 堆区
            // 原因:因为用到了外部变量,而block准备好的代码,在`需要的时候`被执行
            // 调用方并不知道 block 什么时候会被调用
            // MRC 开发,block 在栈区,因为 MRC 的所有内存管理由程序员负责
            // ARC 开发,block 之所以在堆区,是因为编译器帮我们实现的
            // 问:为什么block属性要用 copy?
            void (^block)() = ^ {
                NSLog(@"hello %d", i);
            };
            NSLog(@"%@", block);
            
            // 在定义 block 属性的时候,必须使用 copy,这样在设置数值的时候,可以将栈区的 block 复制到堆中
            self.myBlock = block;
            
            NSLog(@"%@", self.myBlock);
        }

         

      • 总结
      • Block 如果里边没有引用外边的任何变量,在全局区
      • Block 如果里边引用外边的任何变量,在堆区
      • MRC 的 Block 在栈区
      • ARC 的 Block 在堆区
posted @ 2015-11-02 22:36  任淏  阅读(921)  评论(0编辑  收藏  举报