Block 阅读笔记

https://www.jianshu.com/p/25a7ba546eac

https://www.jianshu.com/p/4e79e9a0dd82

看了"小胖白兔"的两篇关于Block的文章,感觉获益良多。

再次总结一下阅读笔记,便于自己查看

1、Block是什么

Block本质上也是一个OC对象,是封装了函数调用以及函数调用上下文环境的OC对象
在这里插入图片描述

2、Block有哪几种

Block有哪几种类型

Block的类型,取决于isa指针,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型

  • __NSGlobalBlock __ ( _NSConcreteGlobalBlock ) 全局块
  • __NSStackBlock __ ( _NSConcreteStackBlock ) 栈块
  • __NSMallocBlock __ ( _NSConcreteMallocBlock ) 堆块

三种block各自的存储域:

  • 全局块存在于全局内存中, 相当于单例.

  • 栈块存在于栈内存中, 超出其作用域则马上被销毁

  • 堆块存在于堆内存中, 是一个带引用计数的对象, 需要自行管理其内存

在这里插入图片描述

如何判断block是哪种类型?

  • 没有访问auto变量的block是__NSGlobalBlock __ ,放在数据段(可以访问全局或者静态变量)

  • 访问了auto变量的block是__NSStackBlock __

  • [__NSStackBlock __ copy]操作就变成了__NSMallocBlock __

注:copy操作可能会隐式调用

3、ARC下,访问外界变量的 Block为什么要自动从栈区拷贝到堆区呢?

栈上的Block,在其所属的变量作用域结束时,该Block就被废弃,如同一般的自动变量。当然,Block中的__block变量也同时被废弃。
为了解决栈块在其变量作用域结束之后被废弃(释放)的问题,我们需要把Block复制到堆中,延长其生命周期。开启ARC时,大多数情况下编译器会恰当地进行判断是否有需要将Block从栈复制到堆,如果有,自动生成将Block从栈上复制到堆上的代码。Block的复制操作执行的是copy实例方法。Block只要调用了copy方法,栈块就会变成堆块。
在这里插入图片描述

什么时候copy

  • 在ARC的Block是配置在栈上的,所以返回函数调用方时,Block变量作用域就结束了,Block会被废弃。种情况编译器会自动完成复制。

  • 在非ARC情况下则需要开发者调用copy方法手动复制。

  • 将Block从栈上复制到堆上相当消耗CPU,所以当Block设置在栈上也能够使用时,就不要复制了,因为此时的复制只是在浪费CPU资源。

    Block的复制操作执行的是copy实例方法。不同类型的Block使用copy方法的效果如下表:

    在这里插入图片描述

在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上的几种情况?
  • 1.block作为函数返回值时
  • 2.将block赋值给__strong指针时
  • 3.block作为Cocoa API中方法名含有usingBlock的方法参数时
  • 4.block作为GCD API的方法参数时

block变量与__forwarding

在copy操作之后,既然__block变量也被copy到堆上去了, 那么访问该变量是访问栈上的还是堆上的呢?__forwarding终于要闪亮登场了。通过/__forwarding, 无论是在block中还是 block外访问__block变量, 也不管该变量在栈上或堆上, 都能顺利地访问同一个__block变量。

__forwarding存在的意义就是:无论在栈还是堆上,forwarding都可以顺利访问到同一个__block变量

__栈上__block的__forwarding指向本身

栈上__block复制到堆上后,栈上block的__forwarding指向堆上的block,堆上block的__forwarding指向本身

在这里插入图片描述

Block 循环引用

Block 循环引用的情况:
某个类将 block 作为自己的属性变量,然后该类在 block 的方法体里面又使用了该类本身。

ARC 下使用三种方式:__weak、__unsafe_unretained、__block

MRC 下使用:__unsafe_unretained、__block

解决办法:
(1)使用 __weak

__weak typeof(self) weakSelf = self;
self.someBlock = ^(Type var){
   [weakSelf dosomething];
};

(2)使用 __block

__block typeof(self) blockSelf = self;
self.someBlock = ^(Type var){
   [blockSelf dosomething];
};

(3) 使用__unsafe_unretained

__unsafe_unretained Person *person = [[Person alloc] init];
person.block = ^{
    NSLog(@"age is %d", weakPerson.age);
};
.三种方法比较
  • __weak:不会产生强引用,指向的对象销毁时,会自动让指针置为nil
  • __unsafe_unretained:不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变
  • __block:必须把引用对象置位nil,并且要调用该block

什么情况使用__block修饰符?

一般情况下,对被截获变量进行 赋值 操作需要添加__block修饰符
赋值 != 使用
赋值操作需要使用 __block修饰

__block NSMutableArray *arrM = nil;
    void (^testBlock)(void) = ^{
        arrM = [NSMutableArray array];
    };
    testBlock();

**使用:**如下代码不需要使用__block,因为是对数组的操作而不是数组的赋值。

NSMutableArray *arrM = [NSMutableArray array];
    void (^testBlock)(void) = ^{
        [arrM addObject:@"addObj"];
    };
    testBlock();

在这里插入图片描述

在这里插入图片描述
那么使用修饰符 __block以后发生了什么呢?

__block修饰变量变成了对象

在这里插入图片描述

__block的内存管理

当block在栈上时,并不会对__block变量产生强引用

block的属性修饰词为什么是copy?

block一旦没有进行copy操作,就不会在堆上
block在堆上,程序员就可以对block做内存管理等操作,可以控制block的生命周期

当block被copy到堆时,对__block修饰的变量做了什么?

  • 会调用block内部的copy函数
  • copy函数内部会调用_Block_object_assign函数
  • _Block_object_assign函数会对__block变量形成强引用(retain)
  • 对于__block 修饰的变量 assign函数对其强引用;对于外部对象 assign函数根据外部如何引用而引用

Block 优缺点

优点:

  • 捕获外部变量
  • 降低代码分散程度

缺点:

  • 循环引用引起内存泄露

Block 总结

  • 在block内部使用的是将外部变量的拷贝到堆中的(基本数据类型直接拷贝一份到堆中,对象类型只将在栈中的指针拷贝到堆中并且指针所指向的地址不变)。
  • __block修饰符的作用:是将block中用到的变量,拷贝到堆中,并且外部的变量本身地址也改变到堆中。
  • __block不能解决循环引用,需要在block执行尾部将变量设置成nil
  • __weak可以解决循环引用,block在捕获weakObj时,会对weakObj指向的对象进行弱引用。
  • 使用__weak时,可在block开始用局部__strong变量持有,以免block执行期间对象被释放。
  • 全局块不引用外部变量,所以不用考虑。
  • 堆块引用的外部变量,不是原始的外部变量,是拷贝到堆中的副本。
  • 栈块本身就在栈中,引用外部变量不会拷贝到堆中。
  • __weak 本身是可以避免循环引用的问题的,但是其会导致外部对象释放了之后,block 内部也访问不到这个对象的问题,我们可以通过在 block 内部声明一个 __strong 的变量来指向 weakObj,使外部对象既能在 block 内部保持住,又能避免循环引用的问题。
  • __block 本身无法避免循环引用的问题,但是我们可以通过在 block 内部手动把 blockObj 赋值为 nil 的方式来避免循环引用的问题。另外一点就是 __block 修饰的变量在 block 内外都是唯一的,要注意这个特性可能带来的隐患。
  • block的实现原理是C语言的函数指针。函数指针即函数在内存中的地址,通过这个地址可以达到调用函数的目的。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值