IOS block原理详解

block的本质:

  • block本质上也是一个OC对象,它内部也有isa指针。
  • block是封装了函数以及函数调用环境的OC对象。
  • block底层结构如下图:
block底层结构

block的变量捕获:

  • 局部变量:
    auto:值捕获。
    static:指针捕获。
  • 全局变量:直接访问。

block的类型:

  • NSGlobalBlock:
    在内存的数据区域.data区,是没有访问auto局部变量。当调用copy后,没有任何的改变,依然在数据区域。
  • NSStacklBlock:
    在内存的栈中,是访问了auto局部变量。当调用copy后,从栈中复制到堆中。
  • NSMallocBlock:
    在内存的堆中,NSStacklBlock调用了copy。当调用copy后,引用计数器+1。

block的copy:

  • 在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上。
    block属性的建议写法:
@property (strong, nonatomic) void (^block)(void);
@property (copy, nonatomic) void (^block)(void);
  • 在MRC环境下,在栈上的block要进行copy操作,复制到堆上。
    block属性的建议写法:
@property (copy, nonatomic) void (^block)(void);

对象类型的auto局部变量:

  • 如果block是在栈上:
    不会对auto局部变量产生强引用。
  • 如果block被copy到堆上:
    1.1 会调用block内部的copy函数
    1.1.1 copy函数内部会调用_Block_object_assign函数
    1.1.1.1 _Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用。
  • 如果block从堆上移除
    1.1 会调用block内部的dispose函数
    1.1.1 dispose函数内部会调用_Block_object_dispose函数
    1.1.1.1 _Block_object_dispose函数会自动释放引用的auto变量(release)。

__block修饰符:

添加__block的变量,编译器会将该变量包装成一个对象,如下图:
包装后
  • 作用:
    __block可以用于解决block内部无法修改auto 局部变量值的问题。(因为auto局部变量在block中是值传递捕获,auto局部变量是在栈中,程序执行完该作用域,就会被销毁,所以需要通过__block把auto局部变量放在堆中,当auto变量加上__block修饰符时,会把该变量封装成一个block结构体,在这个结构体中有个forwarding指针,指向自身,并且这个结构体中包含有auto变量)
  • 注意:
    __block不能修饰全局变量、静态变量(static)。

对象类型的__block局部变量

  • 如果__block是在栈上:
    不会对指向的对象产生强引用。
  • 如果__block被copy到堆上:
    1.1 会调用__block内部的copy函数
    1.1.1 copy函数内部会调用_Block_object_assign函数
    1.1.1.1 _Block_object_assign函数会根据__block变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用。(注意:这里仅限于ARC时会retain,MRC时不会retain)
  • 如果block从堆上移除:
    1.1 会调用block内部的dispose函数
    1.1.1 dispose函数内部会调用_Block_object_dispose函数
    1.1.1.1 _Block_object_dispose函数会自动释放引用的__block变量(release)。
  • __block的_forwarding指针:
    _forwarding指针

block循环引用:

对象持有block,block中又持有对象自己,会造成对象和block无法释放。

解决方案:

  • 在ARC中
    方法一:可以使用__weak修饰符(不会产生强引用,指向的对象销毁时,会自动让指针置为nil)。
__weak修饰符

方法二:可以使用__unsafe_unretained修饰符(不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变)。

__unsafe_unretained修饰符

方法三:可以使用__block修饰符(必须调用block,并在执行完block中的内容后,把修饰的该对象置为nil)。

__block修饰符
  • 在MRC中(在MRC情况下,不支持弱引用__weak)
    方法一:可以使用__unsafe_unretained修饰符(不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变)。
__unsafe_unretained

方法二:可以使用__block修饰符(因为在MRC中,__block不会对持有对象进行强引用retain)。

__block修饰符
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

相关阅读更多精彩内容

  • 原创分享第310天 周二 郑州 小雨 时下流行一句话如是说:为什么我懂得很多道理,却仍然过不好这一生! ...
    禾雨分享阅读 1,375评论 1 1
  • 文/袁非 写下这个标题的时候,我的心是淡然的。当然也不是一开始,就已经放下。是因为我自回到家,连续看了一晚上的书。...
    袁非非也阅读 1,539评论 12 40
  • 2019年12月18日 星期三 天气晴 昨天贾昊然看图写话得了10分,老师奖励了一个大拇指,昨天晚上跟我...
    奋斗_2d50阅读 192评论 0 0
  • 只想一生陪你走 ——送给我的闺蜜 陪你, 时间总是不长, 感觉转瞬即逝。 陪你, 时间总是不短, 眨眼间...
    MAY聆听诗语阅读 277评论 14 10
  • 引入jQuery到项目,使用大公司的CDN服务 CDN的全称是Content Delivery Network,即...
    亭止阅读 251评论 0 0

友情链接更多精彩内容