美文网首页
2018-03-21

2018-03-21

作者: Toki_Han | 来源:发表于2018-03-22 12:53 被阅读8次

从OC—Block到Swift—Closure

写在前面

阅读别人的代码,特别是一些被广泛认可的开源项目的源码,是快速提升自己能力的重要途径。由于个人编程风格的问题,有的大神写的代码就比较晦涩难懂,而且跳跃性很强,经常以跟不上ta的思路而宣告GG。对于我自己来说,我是比较不喜欢阅读Block/Closure特别多的代码的,用着很舒心,读着很糟心。。。
应该有很多(?)和我一样被无数Block或者闭包导致阅读过程中出现不安感的同学,在重新查阅了一些之前的笔记和大神们的资料后,斗胆在这里做一个总结,希望能为读到这里的同学们提供些许的念头通达。
篇幅略长,尽量不赘述,前面是block,中间是closure,最后一些tips,请选择性观看。

利益相关

本文原创,其中有引用别人帖子/博客/回答/etc, 参考内容较多不一一列举,如果您发现有侵权行为或者在意没有注明引用,请@龙傲天联系我等我看到邮件再说 我会在第一时间更正。
时刻保持您的谨慎与怀疑,因为我说的并不是真理。
本文不保留任何权益,开心就好。

概述

Block(代码块)是苹果在iOS4开始引入的对C语言的扩展,是一种特殊的数据类型。它本身封装了一段代码并将这段代码当做变量,通过block()的方式进行回调。
Closure(闭包)是Swift在吸收众家之长的基础上的特性之一,是自包含的功能代码块,可以在代码中使用或者用来作为参数传值。

这里做个简单介绍:

Block的功能:

  1. 定义变量
  2. 作为参数
  3. 作为返回值
  4. 保存一段代码,在需要的时候调用

Closure的功能:

Swift中的闭包使用情况远比Objective-C中的多,以至于闭包就是Swift的基本特性之一,和String/Array是一样的使用频率。
Closure的功能大于等于Block的功能,并有更多的优化。

::注:::Closure != Block,即闭包不是Block的Swift版。

block的声明和赋值

相信很多人(也许只有我)很难在不参考任何资料或者在编辑器的支持下正确的定义一个block,这时推荐Xcode自带的代码块来声明它:

// block声明
返回值类型(^block名称)(参数列表)
// block赋值
Block变量 = ^(参数列表){函数体};

// block声明同时赋值
返回值类型(^block名称)(参数类型) = ^(参数列表) {
    内部的实现方法 / 参数处理 / 想要在别处调用而保存的代码
    };

光说不练假把式,现在我们来写一个block让它来”HelloWorld”:

// 可以新建一个工程,然后在 viewDidLoad 中进行简单测试,开始吧

// 声明一个无返回值,参数为两个字符串对象,叫做myBlock的Block
void(^myBlock)(NSString *a, NSString *b);
// 形参变量名称可以省略,只留有变量类型即可(这个不是必要操作,但是要知道可以这样写)
void(^myBlock)(NSString *, NSString *);

// 给myBlock赋值
myBlock = ^(NSString *a, NSString *b) {
        NSLog(@"%@ %@",a,b);
    };

// 调用myBlock
myBlock(@"hello",@"world");

// 控制台输出结果
"hello world"

更为复杂的block也无非就是在这个基础上改变参数列表,增加函数体内的方法。Is it easy?

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

对于block的底层原理在这里不做探究, 因为其实我也没研究懂 因为本文重点是面向实际使用过程的,有兴趣的同学可以自行查询。
传送门: block源码下载
唐巧—谈Objective-C block的实现

block特性

  • block被Objective-C看成是对象来处理
  • block对于外部变量默认是只读属性
  • block的代码是内联的,效率高于函数调用
  • block的声明与赋值只是保存了一段代码段,必须调用才能执行内部代码

使用typedef定义Block类型

在实际使用Block的过程中,我们可能需要重复地声明多个相同返回值,相同参数列表的Block变量,如果总是重复地编写一长串代码来声明变量会非常繁琐,所以我们可以使用typedef来定义Block类型(还记得之前我们的myBlock吗,现在把它定义成一个类型):

    // 将下面这个接受两个NSString参数,返回为空的block类型定义为名字为MyBlock的类型
    typedef void(^MyBlock)(NSString *, NSString *);
    // 使用的时候指定要使用MyBlock类型的,新建一个该类型的block对象,并定义它的工作内容
    MyBlock myNewBlock = ^(NSString *a, NSString *b) {
        NSLog(@"%@ %@",a,b);
    };
    // 让block开始工作
    myNewBlock(@"hello",@"myNewBlock");
      // 输出结果
      "hello myNewBlock"

      // 再次创建一个MyBlock类型的对象,并给它定义与之前不同的工作内容
    MyBlock anotherBlock = ^(NSString *str1, NSString *str2) {
        NSLog(@"only print str2: %@",str2);
    };
    // 让block开始工作
    anotherBlock(@"hello",@"world");
      // 输出结果
    "only print str2: world"

Block作为函数参数

之前的情形是直接调用block,让它执行自己内部封装的方法,更多的时候是将block当做参数传递并进行操作的,继续我们的例子:

相关文章

网友评论

      本文标题:2018-03-21

      本文链接:https://www.haomeiwen.com/subject/bsbvqftx.html