前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关于Block

关于Block

作者头像
honey缘木鱼
发布2019-12-16 17:44:23
4900
发布2019-12-16 17:44:23
举报
文章被收录于专栏:娱乐心理测试

1.概述

定义: Block是将函数及其执行上下文封装起来的对象。 特点:

  • 可以嵌套定义,定义Block的方法和定义函数的方法相似;
  • Block可以定义在方法内部或外部;
  • 只有调用Block时,才会执行{}中的代码;
  • 本质是对象,是代码高聚合;

优点:捕获外部变量和降低代码分散程度。 缺点:引起循环引用造成内存泄漏。

使用:

代码语言:javascript
复制
声明
typedef int (^myBlock)(int , int);

定义属性
@property (nonatomic,copy) myBlock myOneBlock;

使用
self.myOneBlock = ^int(int a , int b) {
        return a+b;
  };

2.Block内存管理

(1).block种类 全局块(NSGlobalBlock):存在于全局内存中, 相当于单例. 栈块(NSStackBlock):存在于栈内存中, 超出其作用域则马上被销毁. 堆块(NSMallocBlock):在于堆内存中, 是带引用计数的对象, 需要自行管理其内存.

(2).判断block储存位置

NSGlobalBlock:不访问外界变量,block就位于全局区,此时对NSGlobalBlock的retain、copy操作都无效。

代码语言:javascript
复制
void(^myBlock)(void) = ^{
        NSLog(@"没有访问外部变量,在全局区");
    };
    myBlock();
 NSLog(@"%@",[myBlock class]);

//__NSGlobalBlock__

NSStackBlock: 在ARC环境下,当我们声明并且定义了一个block,当该block访问外界变量时,系统帮我们完成了copy操作:NSStackBlock转变到NSMallocBlock的过程,将栈区的block移到堆区来延长block的生命周期。但使用了__weak或者__unsafe__unretained的修饰符,那么系统就不会为我们做copy的操作,不会将其迁移到堆区。

代码语言:javascript
复制
    int a = 10;
    __weak void(^myBlock)(void) = ^{
        NSLog(@"访问外部变量,用__weak修饰,在栈区%d",a);
    };
    myBlock();
    NSLog(@"%@",[myBlock class]);

//__NSStackBlock__

NSMallocBlock: 在ARC环境下,__strong修饰的(默认)block只要捕获了外部变量就会位于堆区。

代码语言:javascript
复制
  int a = 10;
     void(^myBlock)(void) = ^{
        NSLog(@"访问外部变量,用默认__strong修饰,在堆区%d",a);
    };
    myBlock();
    NSLog(@"%@",[myBlock class]);

//__NSMallocBlock__

3.block访问外部变量

1.截获局部变量 (1).默认情况

代码语言:javascript
复制
  int  c= 10;
    self.MyBlock = ^(int a , int b) {
        return a+b+c;
    };
    c= 15;
    NSLog(@"%d",self.MyBlock(10,10));
//结果:30

(2).用__block 或static

代码语言:javascript
复制
static  int  c= 10; 或
 __block  int  c= 10;
    self.MyBlock = ^(int a , int b) {
        return a+b+c;
    };
    c= 15;
    NSLog(@"%d",self.MyBlock(10,10));
//结果:35

总结:局部变量放在堆区,默认情况下block只copy变量的数值,所以只能访问不能修改。对于__block ,block是复制引用地址来实现访问,所以即可访问也可修改局部变量的值,用static修饰的静态变量也是以指针形式访问。

代码语言:javascript
复制
-------------------------默认----------------
    int  c= 10; //默认
    NSLog(@"block定义前c地址:%p",&c);
    self.MyBlock = ^(int a , int b) {
        NSLog(@"block定义内部c地址:%p",&c);
        return a+b+c;
    };
    c= 15;
    NSLog(@"block定义后c地址:%p",&c);
    NSLog(@"%d",self.MyBlock(10,10));

    结果:
     block定义前c地址=0x16ef938ac
     block定义后c地址=0x16ef938ac
     block定义内部c地址=0x2834343e0

    总结:
     block定义前:c在栈区
     block定义内部:里面的c是根据外面的c拷贝到堆中的,不是一个c
     block定义后:c在栈区


--------------------用__block 或static---------------------

      __block int  c= 10;
    NSLog(@"block定义前c的地址:%p",&c);
    self.MyBlock = ^(int a , int b) {
        NSLog(@"block定义内部c的地址:%p",&c);
        return a+b+c;
    };
    c= 15;
    NSLog(@"block定义后c的地址:%p",&c);
    NSLog(@"%d",self.MyBlock(10,10));

   结果:
     block定义前c的地址:0x16ed838a8
     block定义后c的地址:0x282438d58
     block定义内部c的地址:0x282438d58

    总结:
      声明 c 为 __block (__block 所起到的作用就是只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。)
     block定义前:c在栈中。
      block定义内部: 将外面的c拷贝到堆中,并且使外面的c和里面的c是一个。
      block定义后:外面的c和里面的c是一个。
      block调用前:c的值还未被修改。
      block调用后:c的值在block内部被修改。

2.全局变量 全局变量和静态全局变量都可访问和修改,不被截获。

4.循环引用

对象强引用Block,而Block又持有这个对象,这样就会产生循环引用。打破循环引用的方法:持有对象的属性进行一个弱引用。

解决方法: (1). weak+strong 强弱引用(也是最常用的方法)

代码语言:javascript
复制
 __weak typeof(self) weakSelf = self;
    self.MyBlock = ^(int a , int b) {
        NSLog(@"%@",weakSelf.str);
    };

(2). __block+nil 临时变量

代码语言:javascript
复制
  __block TestViewController*weakSelf = self;
    self.MyBlock = ^(int a , int b) {
        NSLog(@"%@",weakSelf.str);
        weakSelf = nil;//一定要把临时变量销毁
    };
  self.MyBlock(10, 10);

(3). block加参

代码语言:javascript
复制
   self.MyBlock = ^(TestViewController*weakSelf) {
        NSLog(@"%@",weakSelf.str);
    };
  self.MyBlock(self);

6.面试问题点

(1).什么情况使用__block修饰符? 一般情况下,对被截获变量进行赋值操作需要添加__block修饰符 赋值 != 使用。

使用:

代码语言:javascript
复制
NSMutableArray *array = [[NSMutableArray alloc]init];
    void(^myBlock)(void) = ^{
        [array addObject:@"123"];
    };
    myBlock();

总结:block 里只是对数组的操作,不是赋值,不需要用__block修饰.

赋值:

代码语言:javascript
复制
__block NSMutableArray *array = nil;
   void(^myBlock)(void) = ^{
       array = [[NSMutableArray alloc]init];
   };
   myBlock();

总结:要用__block修饰,因为数组是赋值操作。

(2).为什么修饰block要用copy? 当我们使用block作为一个对象的属性,需要用copy来修饰block,因为它在栈区,函数执行完会立即释放,block只有经过copy才会从栈区移到堆区,我们就可以自己控制block的生命周期。 Block copy操作之后

(3).gcd的block什么时候销毁?

代码语言:javascript
复制
Person *person = [[Person alloc]init];
   person.name = @"hello";
   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
       NSLog(@"%@",person.name);
   });
Person.m
-(void)dealloc{
   NSLog(@"person被销毁");
}

hello
person被销毁

总结:gcd的block默认会做copy操作,即dispatch_after的block是堆block,block会对Person强引用,block销毁时候Person才会被释放。

代码语言:javascript
复制
 Person *person = [[Person alloc]init];
    person.name = @"hello";
    __weak Person*weakPerson = person;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"%@",weakPerson.name);
    });
person被销毁
(null)

总结:使用__weak修饰过后的对象,堆block会采用弱引用,所以在函数结束后,Person就会被释放,5秒后gcd就无法捕捉到Person。

代码语言:javascript
复制
Person *person = [[Person alloc]init];
    person.name = @"hello";
   
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"1-------%@",person.name);
         __weak Person*weakPerson = person;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"2——————————%@",weakPerson.name);
        });
    });
1-------hello
person被销毁
2——————————(null)

总结:gcd内部只要有强引用Person,Person就会等待执行完再销毁,不会等待后执行的弱引用,会直接释放的。

(4).为什么UIView 的 animation动画块使用了Block,内部使用self不会循环引?

代码语言:javascript
复制
 [UIView animateWithDuration:2.0 animations:^{
        NSLog(@"%@",self.str);
    }];

因为UIView 动画块是类方法,不被self持有,所以不会循环引用。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.概述
  • 2.Block内存管理
  • 3.block访问外部变量
  • 4.循环引用
  • 6.面试问题点
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档