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中的闭包

  • 相关阅读:
    self 和 super 关键字
    NSString类
    函数和对象方法的区别
    求两个数是否互质及最大公约数
    TJU Problem 1644 Reverse Text
    TJU Problem 2520 Quicksum
    TJU Problem 2101 Bullseye
    TJU Problem 2548 Celebrity jeopardy
    poj 2586 Y2K Accounting Bug
    poj 2109 Power of Cryptography
  • 原文地址:https://www.cnblogs.com/zhouDongdong/p/9354236.html
Copyright © 2011-2022 走看看