zoukankan      html  css  js  c++  java
  • IOS 浅谈闭包block的使用

    前言:对于ios初学者,block通常用于逆向传值,遍历等,会使用,但是可能心虚,会感觉block很神秘,那么下面就一起来揭开它的面纱吧。

    ps: 下面重点讲叙了闭包的概念,常用的语法,以及访问变量,循环引用问题,至于底层的运行,堆栈block的区别,还有其他用法这里就不介绍了,目前也处于迷糊中,等到真正理解了再来补充 - -。

    一. 概念 

    1. 什么是闭包?

    闭包就是能够读取其他函数内部变量的函数,可以理解成“定义在一个函数内部的函数“。

    在本质上,闭包是将函数内部和函数外部连接起来的桥梁

    闭包在很多语言中都有应用,C,JAVA,OC等

    2. OC中的Block

    在OC中,Block是在iOS4开始引入,是对C语言的扩展,被用来实现匿名函数的特性

    Block是一种特殊的数据类型,可以正常定义变量、作为参数、作为返回值

    特殊地,Block还可以声明赋值去保存一段代码,在需要调用的地方去调用

    目前Block已经广泛应用于各类回调传值、排序遍历、GCD、动画等

    二. 基本语法

    1. block做自由变量 - 声明、赋值以及调用 (个人感觉理解语法就好)

     1 // 声明一个名字为 TestBlockTest 的无返回值含有参数的变量
     2     void (^TestBlockTest)(NSString *);
     3 // 只声明变量,需要赋值
     4     TestBlockTest = ^(NSString *parameter){
     5         NSLog(@"测试");
     6     };
     7 // 调用
     8     TestBlockTest(@"");
     9     
    10 // 声明TestTwoBlock变量同时赋值
    11     int (^TestTwoBlock)(int) = ^(int num){
    12         return num*8;
    13     };
    14 // 已经声明了blcok并赋值了 ,可以直接调用
    15     int num = TestTwoBlock(8);
    16     NSLog(@"%d",num);

    注意:

    ^ 这个叫做 脱字符,其中,返回值类型,参数列表可以省略简写,这个用多了就知道了,开始推荐写全,基础要扎实

    第一个输出值 测试,没有用到参数parameter,强迫症大神请见谅哈!第二个输出值 64

    Block的声明与赋值只是保存了一段代码段,必须 调用 才能执行内部代码

    2. 使用typedef定义Block类型

    可做属性,可做参数、返回值类型等 (这个用法务必掌握)

    示例代码背景:A页面点击按钮 跳转 B页面,B页面返回A页面时候,传值@“测试”,用于修改A页面按钮名字

    2.1 .h中定义

     1 #import <UIKit/UIKit.h>
     2 
     3 // typedef 定义无返回值,有一个参数,名字为TestBlock的block类型
     4 typedef void(^TestBlock)(NSString *);
     5 
     6 @interface ViewController : UIViewController
     7 
     8 // 做属性
     9 @property (nonatomic, copy) TestBlock testBolck;
    10 
    11 // 做方法参数
    12 - (void)returnText:(TestBlock)block;
    13 
    14 @end

    2.2 .m中用法,实现(下面整合了做属性,做参数的代码)

     1 // B页面
     2 
     3 // 做属性
     4     TestBlock blockVar = ^(NSString *parameterTwo){
     5         NSLog(@"hello world %@",parameterTwo);
     6     };
     7  /*
     8      * 我们可能需要重复地声明多个相同返回值相同参数列表的Block变量
     9      * 如果总是重复地编写一长串代码来声明变量会非常繁琐
    10      * 所以我们可以使用typedef来定义Block类型
    11      * 然后像OC中声明变量一样使用Block类型 TestBlock 来声明变量
    12 **/
    13     blockVar(@"UZI");
    14     blockVar(@"");
    15 // 实现定义的方法 16 - (void)returnText:(TestBlock)block{ 17 self.testBolck = block; 18 }
    19 // 这里选择返回传参数,用法很多,看个人喜好 20 - (void)viewWillDisappear:(BOOL)animated{ 21 self.testBolck(@"测试"); 22 }
    23 // A页面 在页面跳转部分 24 // 做属性回调 25 __weak __typeof(self) weakSelf = self; 26 vc.testBolck = ^(NSString *parameter) { 27 [weakSelf setBtnTitle:parameter]; 28 }; 29 30 // 做参数回调 31 [vc returnText:^(NSString *parameter) { 32 [self setBtnTitle:parameter]; 33 }];
    34 // 做参数另一种写法 35 __weak __typeof(self) weakSelf = self; 36 [vc returnText:^(NSString *parameter) { 37 __strong __typeof(weakSelf) strongSelf = weakSelf; 38 // 这里用strong保证self不被释放,详见下文 循环引用weak,strong修饰问题 39 [strongSelf setBtnTitle:parameter]; 40 }];

    注意:

    上面选择在B页面返回A页面时候传递参数,传递给A,A页面分别用了block做属性,做参数是怎么完成逆向传值的,方式很多,凭自己喜好

    三.   block访问变量问题

    这里不一一代码举例,个人感觉看总结,主要有以下四点,记住就好

    1.   Block拥有捕获外部变量的功能,在Block中访问一个外部的局部变量,Block会持用它的临时状态,自动捕获变量值,外部局部变量的变化不会影响它的的状态,可以理解为瞬间性捕获。

    2.  在block中,可以访问局部变量(自由变量),但是不能修改局部变量,因为:block捕获的是自动变量的const值,名字一样,不能修改

    3.  可以访问静态变量,并修改变量的值,静态变量属于类的,不是某一个变量,因此block不用调用self指针,所以block可以修改值

    4.  使用__block修饰符的局部变量,可以修改局部变量的值。包括可变类型的参数,也可以修改,这个可以用clang命令将OC转为C++代码来查看一下Block底层实现

    四:  循环引用 __weak __strong 修饰问题

    很多初学者对这块都是模糊的,只知道加上 __weak __typeof(self) weakSelf = self 这句,弱引用,可以防止循环引用。

    那什么是循环引用?

    简单理解为 相互持有强引用,造成block内所持有的对象无法释放,引起内存泄漏

    当然,造成循环引用不唯一,好比对象内部有一个Block属性,而在Block内部又访问了该对象,那么也会造成循环引用

    下面分三点来谈论:

    1. 如果相互持有强引用,即对象内部有一个Block属性,而在Block内部又访问了该对象,那么会造成循环引用。

    解决办法是使用一个弱引用的指针指向该对象,然后在Block内部使用该弱引用指针来进行操作,这样引用计数不加1,避免了Block对对象进行强引用。

    通常是这样: __weak __typeof(self) weakSelf = self

         注意: 以上是在ARC情况下,如果MRC中,可以在 会引起相互持有的对象 前面,使用 __block 修饰,原理是可以禁止block对对象进行retain操作,引用计数不会加1,从而解决循环引用问题。

         这种循环引用示例(部分代码,提供思路):

     1 // 定义一个block
     2      typedef void(^HYBFeedbackBlock)(id model);
     3 // 声明一个对象
     4      @property (nonatomic, strong) HYBAView *aView;
     5 // block做方法参数
     6      - (instancetype)initWithBlock:(HYBFeedbackBlock)block;
     7  // 构造方法
     8      - (instancetype)initWithBlock:(HYBFeedbackBlock)block {
     9      if (self = [super init]) {
    10      self.block = block;
    11      return self;
    12      }
    13 // 调用
    14      self.aView = [[HYBAView alloc] initWithBlock:^(id model) {
    15      // 假设要更新model
    16      self.currentModel = model;
    17      }];

    上面代码很容易看出所形成的环:

         vc->aView->block->vc(self)

         vc->aView->block->vc.currentModel

    2. 对于上面的那种情况,为消除循环引用,而用弱引用

         虽说使用__weak,但是此处会有一个隐患,你不知道 block内的 self 什么时候会被释放,

         为了保证在block内不会被释放,我们添加__strong,更多的时候需要配合strongSelf使用

    1    // 上面讲到做方法参数时候的一种写法
    2 __weak __typeof(self) weakSelf = self;
    3 [vc returnText:^(NSString *parameter) {
    4     __strong __typeof(weakSelf) strongSelf = weakSelf;
    5     [strongSelf setBtnTitle:parameter];
    6 }];

    可能有人问,用strong,那什么时候才释放呢?

         用修饰符strong时,当外部把变量/对象释放掉,但block如果没有执行结束,那么系统就会等待block执行完成后再释放,

         对该变量/对象在block中的使用起到了保护作用,当block执行结束后会自动释放掉(ARC)。

         不过若无强烈需求,不建议在Block里加strong,容易占用内存,造成内存消耗

    3. 是不是所有的block都要用弱引用呢?

      不是,如果没有相互直接引用,可以放心大胆的不用__weak

      并不是block就一定会造成循环引用,如果不是相互持有,可以不用__weak 去弱引用

          最经典的示例: Masonry代码布局

    1  [self.headView mas_makeConstraints:^(MASConstraintMaker *make) {
    2 
    3         make.centerY.equalTo(self.otherView.mas_centerY);
    4   }];

      * block里用到了self,block会保持一个对self的引用,但是self并没有直接或者间接持有block,所以不会造成循环引用

      形成的持有链:

          self ->self.headView ··· MASConstraintMaker构造block->self

    五. 感兴趣的可以继续了解下

    *  block 与内存管理,堆栈block等

    *  block 底层实现

    *  block 其他用法

    总结: 认清一件事物需要长期不断的观察与思考,长路漫漫修远兮,时刻保持学习心,与君共勉

    参考文献:

    一篇文章看懂iOS代码块Block

    block 中使用__weak 和__strong修饰符的问题

    iOS中Block的用法,举例,解析与底层原理(这可能是最详细的Block解析)

    iOS闭包循环引用精讲

    深入解构iOS的block闭包实现原理 (想看block实现原理推荐看此文献)

    浅谈iOS中的闭包

  • 相关阅读:
    mongoDB--1 概念
    PythonWeb-Django框架学习-Demo2-创建你的小WEB应用
    PythonWeb-Django框架学习-Demo1-快速搭建项目
    Python--Demo18--异步IO之协程
    Python--Demo17--WEB应用程序之模板和MVC
    Python--Demo16--WEB应用程序之框架的好处
    Python--Demo15--WEB应用程序WGSI接口
    快速了解HTTP协议
    Python--Demo14--正则表达式
    Python--Demo13--高级特性匿名函数、推导式、迭代器、生成器
  • 原文地址:https://www.cnblogs.com/zhouDongdong/p/9354236.html
Copyright © 2011-2022 走看看