Swift中闭包实战案例详解

前言

无论苹果的官方文档还是由官方文档衍生出来的一些文章和书籍都比较重视基础语法知识的讲解,对于实战中的应用提及的都很少,所以当我们想使用“闭包”解决一些问题的时候,会忽然出现看着一堆理论知识却不知从何下手的尴尬感,这就是理论和时实战的区别了。

本文不赘述Swift闭包的的基本语法了,百度或者Google下有很多资料。如题所示本文着重讲述Swift闭包的一些实战案例,有需要的小伙伴可以参考下,经验丰富的大神也请指教。

关于如何理解闭包

学习闭包的第一个难点就是理解闭包,可能很多人用了很久的闭包都还不太清楚闭包到底是什么,我这里提供一种理解思路,仅供参考。

对于很多iOS开发者来说一开始接触到Swift闭包会试图用OC中的Block去理解,当然这会对我们的理解有一定帮助,就好比很多人学习英语:tomato->西红柿->🍅,而不是tomato-> 🍅,而一个婴儿刚开始接触语言时候就是直接由tomato的发音联想到🍅,而后再去学习🍅的单词拼写,这是人类与生俱来的学习语言的逻辑流程,所以我们不妨按照这种思维逻辑去学习和理解Swift的闭包。

1、闭包是什么

这就好比一个婴儿好奇西红柿是什么,家长会将一个真实的西红柿拿到他的面前,看一看,摸一摸,闻一闻,尝一尝。对于Swift的闭包,我们首先不需要知道是它在语法上如何定义的,而是要知道闭包的本质。

闭包的本质是代码块,它是函数的升级版本,函数是有名称、可复用的代码块,闭包则是比函数更加灵活的匿名代码块。

2、为什么需要闭包

当一个婴儿知道了西红柿是什么,自然而然就会想到西红柿有什么用,那么我们自然也会问闭包在Swift中有何用处呢?

函数已经可以满足我们开发中大部分的需求了,那么为什么还需要闭包呢。在开发中我们经常需要传递各种数据,我们习惯了传递一个值:Int,一串符号:String,一个对象:Class,但是有时我们需要传递一种处理问题的逻辑,我们常用的类型似乎满足不了这种需求,而函数恰好是一种处理问题的逻辑,为了让函数像Int、Float、String等常用类型一样灵活的传递和调用,闭包就出现了。

综上所述,我们可以知道闭包本质上和函数一样都是代码块,而闭包更加灵活。

闭包、嵌套函数、函数

更好地使用闭包前需要理清3者的联系和区别

首先看3种函数的定义:

//函数
func eatTomatos(a: Int, b: Int) -> Int {
 return a + b
}

//嵌套函数
func eatTomatos(a: Int, b: Int) -> Int {
 //嵌套函数
 func digest(a: Int, b: Int) -> Int {
  return 2 * a + b
 }

 return digest(a: a, b: b)
}

//闭包
var eatTomatos = {(a: Int, b: Int) -> Int in
 return a + b
}

从上面的定义可以看出函数和嵌套函数其实是一回事,唯一的区别是,嵌套函数是定义在一个函数内部的函数,对外部是隐藏的,只能在其定义的函数内部有效。而闭包与函数的不同要多一些:1、不需要使用func关键字,2、其次函数有名称如:eatTomatos,而闭包是没有名称的,3、闭包的参数和函数体都要使用{ }包起来,在参数后要使用in关键字连接函数体,4、闭包可以作为一种类型赋值给一个变量,上面代码中的闭包类型是: (Int, Int) -> Int

上面从定义上分析了3者的不同,下面从功能上区分下。

1、函数是全局的,不能捕获上下文中的变量;而嵌套函数和闭包可以直接嵌套在上下文中使用的,因此可以捕获上下文中的变量,需要注意的是每一个闭包都只会持有一个它所捕获的变量的副本,如下:

override func viewDidLoad() {
  super.viewDidLoad()
  print(eatTomatos(a: 1, b: 2))//③
  print(eatTomatos(a: 2, b: 3))//④
}

func eatTomatos(a: Int, b: Int) -> Int {
 var numArray: Array<Int> = Array.init()

 //嵌套函数
 func digest(a: Int, b: Int) -> Int {
  numArray.append(a)
  numArray.append(b)
  print(numArray.count)//②
  return 2 * a + b
 }

 print(numArray.count)//①

 return digest(a: a, b: b)
}

//打印的结果依次(①②③④)是:
0
2
4
0
2
7

2、闭包可以作为参数或者返回值,如下:

// 作为参数
override func viewDidLoad() {
 super.viewDidLoad()
 cookTomates { (a, b) in
  print(a)
  print(b)
 }
}

func cookTomates(tomato: (Int, Int) -> Void){
 tomato(1, 2)
}

cookTomates函数将闭包(Int, Int) -> Void作为参数,并且可以在函数内部操作这个闭包

在调用cookTomates函数式需要将给这个闭包参数赋值,并且闭包中的参数名需要调用的时候自行命名。

//作为返回值
override func viewDidLoad() {
 super.viewDidLoad()

 let tomato = gainTomatos()
 print(tomato(2, 3))

}
var eatTomatos: (Int, Int) -> Int = {(a: Int, b: Int) -> Int in
 return a + b
}
func gainTomatos() -> (Int, Int) -> Int {
 return eatTomatos
}

函数gainTomatos将闭包(Int, Int) -> Int作为返回值,这里返回的是(Int, Int) -> Int的一个实例,调用者便可以利用返回的实例获取(Int, Int) -> Int闭包处理参数的逻辑,实现代码的传递和复用

为你的闭包类型起别名

闭包类型不像其他常用类型看起来比较简洁,有参数、返回值、关键字、符号构成,影响阅读和纠错,因此为常用的闭包类型起一个别名很有必要。

如下,为(Int, Int) -> Int闭包类型起别名

typealias Tomato = (Int, Int) -> Int

因此上面闭包当做返回值使用的代码便可以改写如下:

override func viewDidLoad() {
 super.viewDidLoad()

 let tomato = gainTomatos()
 print(tomato(2, 3))

}

var eatTomatos: Tomato = {(a: Int, b: Int) -> Int in
 return a + b
}

func gainTomatos() -> Tomato {
 return eatTomatos
}

当我们把(Int, Int) -> Int类型抽象为Tomato后,不仅仅是代码看起来更加简洁,也更接近我们使用的其他参数类型,更加便于理解

闭包传值

OC中常用的传值方法有代理、Block、通知等,对应到Swift Block就由闭包替代。

如下需要使用闭包将B中的a、b值传递到A中

override func viewDidLoad() {
 super.viewDidLoad()

 let a: A = A()
 a.fromB()

}

typealias Tomato = (Int, Int) -> Int

class A: NSObject {
 let b: B = B()

 func fromB() {
  b.tomato = {
   (x, y) -> Int in
   return x + y
  }
  print(b.toA())
 }

}

class B: NSObject {
 var tomato: Tomato?

 func toA() -> Int {
  let a = 3
  let b = 4
  return tomato!(a, b)
 }

}

由上可以总结出闭包传值的流程:

1️、首先为自己的闭包类型起一个别名,便于使用;

2️、在需要把值传递给另外一个对象的类里声明一个闭包类型的变量,对应到上面的代码中就是B;

3️、在需要接收值的类里为闭包类型赋值,从而在此闭包内便可以获取传递的值。

注意:

这里着重描述传值的流程,在开发的时候还需判断闭包是否为nil,否则会导致崩溃;

闭包作为参数传值

在使用AFN或者SDWebImage的时候,通过Block获取请求的数据很方便,那么在Swift中如何使用闭包实现这种效果呢。

其实上面在说闭包作为参数使用的时候,已经实现了这种传值的方式,这里举另外一个例子,我们在使用第三方库的时候通常会将其再封装一次,避免由于第三方库不维护或者出现较大更新的时候增加不必要的工作量,这里以简单封装Alamofire为例,代码如下:

import UIKit
import Alamofire
import SwiftyJSON

class ZYLResponse: NSObject {
 //接收数据是否成功
 var isSuccess: Bool = false
 //接收到的字典数据
 var dict: Dictionary<String, Any>?
 //接收到的数组数据
 var array: Array<Any>?
 //错误信息
 var error: Error?
 //JSON
 var json:JSON?

}

typealias DataReply = (ZYLResponse) -> Void

class ZYLNetTool: NSObject {

 ///POST请求
 open static func post(url: String, parameters: Dictionary<String, Any>?, complete: @escaping DataReply) {
  Alamofire.request(url, method: .post, parameters: parameters).responseJSON { (response) in
   let myResponse = ZYLResponse()
   myResponse.isSuccess = response.result.isSuccess
   myResponse.dict = response.result.value as! Dictionary<String, Any>?
   myResponse.array = response.result.value as? Array<Any>
   myResponse.error = response.result.error
   myResponse.json = JSON(data: response.data!)

   complete(myResponse)
  }
 }

 ///GET请求
 open static func get(url: String, parameters: Dictionary<String, Any>?, complete: @escaping DataReply) {
  Alamofire.request(url, method: .get, parameters: parameters).responseJSON { (response) in
   let myResponse = ZYLResponse()
   myResponse.isSuccess = response.result.isSuccess
   myResponse.dict = response.result.value as! Dictionary<String, Any>?
   myResponse.array = response.result.value as? Array<Any>
   myResponse.error = response.result.error
   myResponse.json = JSON(data: response.data!)

   complete(myResponse)
  }
 }

}

//调用
  ZYLNetTool.post(url: HTTP_ACTIVATE_PORT, parameters: paraDict, complete: { (response) in
   if response.isSuccess {
    //请求数据成功

   } else {
    //请求数据失败

   }
  })

注意:

1、使用闭包时要注意管理内存;

2、当作闭包为函数参数使用时可以脱离函数独立使用时,要将此闭包声明为逃逸闭包,在参数类型前面加上@escaping,否则会报错。

总结

接触一种新的事物之前总会觉得很难,当我们学会后发现其实很简单,难的不是这个新事物本身,而是我们的大脑出于习惯很难接受新的事物,总是需要一定的过程。记得学C语言时很难理解指针,学C++时很难理解面向对象,学OC时很难理解Block,而Swift作为一种新的语言,必然会有很多新的事物让我们难以理解,比如闭包、元组、可选类型、函数式编程等等,以上就是这篇文章的全部内容了,本文只对闭包发表一点拙见,如果有什么不足的地方还望指正,谢谢。

(0)

相关推荐

  • 深入理解Swift语言中的闭包机制

    在 Swift 中的闭包类似于结构块,并可以在任何地方调用,它就像 C 和 Objective C 语言内置的函数. 函数内部定义的常数和变量引用可被捕获并存储在闭包.函数被视为封闭的特殊情况,它有 3 种形式. 在 Swift 语言闭合表达式,如下优化,重量轻语法风格,其中包括: 推导参数并从上下文菜单返回值的类型 从单封表达的隐性返回 简略参数名称 尾部闭包语法 语法 下面是一个通用的语法定义用于闭包,它接受参数并返回数据的类型: 复制代码 代码如下: {(parameters) -> re

  • Swift教程之闭包详解

    闭包(Closures)是独立的函数代码块,能在代码中传递及使用.Swift中的闭包与C和Objective-C中的代码块及其它编程语言中的匿名函数相似. 闭包可以在上下文的范围内捕获.存储任何被定义的常量和变量引用.因这些常量和变量的封闭性,而命名为"闭包(Closures)".Swift能够对所有你所能捕获到的引用进行内存管理. NOTE 假如你对"捕获(capturing)"不熟悉,请不要担心,具体可以参考Capturing Values(捕获值). 全局函数

  • Swift 中闭包的简单使用

    本文主要是介绍Swift中闭包的简单使用,将从"闭包的定义"."闭包的创建.赋值.调用"."闭包常见的几种使用场景","使用闭包可能引起的循环强引用" 四个方面入手,重点介绍闭包如何使用,没有高深的概念,只是专注于实际使用,属于入门级水平,后面还会有关于闭包更加详细和深入理解的文章.希望大家在阅读完本文后能够对闭包有一个整体的理解以及能够简单的使用它. 闭包的定义 在Swift开发文档中是这样介绍闭包的:闭包是可以在你的代码中

  • IOS swift3.0 下闭包语法整理

    IOS swift3.0 下闭包语法整理 一.闭包的概念 有oc基础的都知道,闭包其实是oc里面的block,语法格式不一样,但作用是一样的.主要是用于callBack(异步回调)或者两个类之间的通信.它的本质一个函数,一个可执行的代码块,只是这个函数是没有名字的,也就是匿名函数.你也可以把他看作如 int.float一样,是一种数据类型,一种可以作为参数传递的数据类型. 二.基本语法 1.闭包的声明 //定义一个求和闭包 //闭包类型:(Int,Int)->(Int) let add:(Int

  • Swift 3.0基础学习之闭包

    前言 闭包是功能性自包含模块,可以在代码中被传递和使用. Swift 中的闭包与 C 和 Objective-C中的 blocks 以及其他一些编程语言中的 lambdas 比较相似.下面这篇文章就来详细介绍了关于Swift 3.0中的闭包,感兴趣的一起来看看吧. 开始 闭包的书写格式如下: { (parameters) -> return type in statements } 如 reversedNames = names.sorted(by: { (s1: String, s2: Str

  • 详解Swift中的函数及函数闭包使用

    一.引言 函数是有特定功能的代码段,函数会有一个特定的名称调用时来使用.Swift提供了十分灵活的方式来创建与调用函数.事实上在Swift,每个函数都是一种类型,这种类型由参数和返回值来决定.Swift和Objective-C的一大区别就在于Swift中的函数可以进行嵌套. 而Swift中的闭包是有一定功能的代码块,这十分类似于Objective-C中的block语法.Swift中的闭包语法风格十分简洁,其作用和函数的作用相似. 二.函数的创建与调用 函数通过函数名,参数和返回值来定义,参数和返

  • Swift中闭包实战案例详解

    前言 无论苹果的官方文档还是由官方文档衍生出来的一些文章和书籍都比较重视基础语法知识的讲解,对于实战中的应用提及的都很少,所以当我们想使用"闭包"解决一些问题的时候,会忽然出现看着一堆理论知识却不知从何下手的尴尬感,这就是理论和时实战的区别了. 本文不赘述Swift闭包的的基本语法了,百度或者Google下有很多资料.如题所示本文着重讲述Swift闭包的一些实战案例,有需要的小伙伴可以参考下,经验丰富的大神也请指教. 关于如何理解闭包 学习闭包的第一个难点就是理解闭包,可能很多人用了很

  • Python中return用法案例详解

    python中return的用法 1.return语句就是把执行结果返回到调用的地方,并把程序的控制权一起返回 程序运行到所遇到的第一个return即返回(退出def块),不会再运行第二个return. 例如: def haha(x,y): if x==y: return x,y print(haha(1,1)) 已改正: 结果:这种return传参会返回元组(1, 1) 2.但是也并不意味着一个函数体中只能有一个return 语句,例如: def test_return(x): if x >

  • Python自动化办公实战案例详解(Word、Excel、Pdf、Email邮件)

    目录 背景 实现过程 1)替换Word模板生成对应邀请函 2)将Word邀请函转化为Pdf格式 4)自动发送邮件 5)完整代码 总结 背景 想象一下,现在你有一份Word邀请函模板,然后你有一份客户列表,上面有客户的姓名.联系方式.邮箱等基本信息,然后你的老板现在需要替换邀请函模板中的姓名,然后将Word邀请函模板生成Pdf格式,之后编辑统一的邀请话术(邮件正文),再依次发送邀请函附件到客户邮箱,你会怎么做? 正常情况下,我们肯定是复制粘贴Excel表格中的客户姓名,之后挨个Word文档进行替换

  • IOS 开发之swift中手势的实例详解

    IOS 开发之swift中手势的实例详解 手势操作主要包括如下几类 手势 属性 说明 点击 UITapGestureRecognizer numberOfTapsRequired:点击的次数:numberOfTouchesRequired:点击时有手指数量 设置属性 numberOfTapsRequired 可以实现单击,或双击的效果 滑动 UISwipeGestureRecognizer direction:滑动方向 direction 滑动方向分为上Up.下Down.左Left.右Right

  • TypeScrip中泛型的案例详解

    泛型的定义 // 需求一: 泛型 可以支持不特定的数据类型, 要求,传入的参数和返回参数一致 // 这种方式虽然能实现传入和返回的参数一致,但是失去类型参数检验 /* function getData(value: any): any { return "success" } */ // 定义泛型解决需求一 // T表示泛型(这里的大写字母可以随便定义,但一般默认为T) 具体什么类型是调用这个方法的时候决定的 function getData<T>(value: T):T{

  • Java jvm中Code Cache案例详解

    Code Cache JVM生成的native code存放的内存空间称之为Code Cache:JIT编译.JNI等都会编译代码到native code,其中JIT生成的native code占用了Code Cache的绝大部分空间 相关参数 Codecache Size Options -XX:InitialCodeCacheSize 用于设置初始CodeCache大小 -XX:ReservedCodeCacheSize 用于设置Reserved code cache的最大大小,通常默认是2

  • IIS服务器中部署PHP案例详解

    部署网站前查看一下系统是否已经安装CGI 1.启动iis服务器,打开IIS服务器 打开IIS服务器,点击网站,右击"添加网站" 2.创建网站 点击"添加网站"后,进入页面填写网站相关内容,如:网站名称.物理路径(网站所在文件夹),点击"确定"创建成功 3.PHP设置 点击创建好的网站,点击"处理程序映射",点击右侧的"添加映射模块",在弹出层中输入对应的参数,点击确认 设置默认文档 点击"默认文档

  • Java BigDecimal中divide方法案例详解

    1.首先说一下用法,BigDecimal中的divide主要就是用来做除法的运算.其中有这么一个方法. public BigDecimal divide(BigDecimal divisor,int scale, int roundingMode) 第一个参数是除数,第二个参数代表保留几位小数,第三个代表的是使用的模式. BigDecimal.ROUND_DOWN:直接省略多余的小数,比如1.28如果保留1位小数,得到的就是1.2 BigDecimal.ROUND_UP:直接进位,比如1.21如

  • Python字典中items()函数案例详解

    Python3:字典中的items()函数 一.Python2.x中items():   和之前一样,本渣渣先贴出来python中help的帮助信息: >>> help(dict.items) Help on method_descriptor: items(...) D.items() -> list of D's (key, value) pairs, as 2-tuples >>> help(dict.iteritems) Help on method_de

  • JavaScript 解决ajax中parsererror错误案例详解

    解决ajax的parsererror错误的终极办法(后台传给前台的数据json问题) 出现这个问题的原因是因为后台传给前台的数据出现了问题,ajax对于json的格式特别的严格 下面是会出现这个问题的ajax请求 $.ajax({ type:'get', url:"{php echo $this->createWebUrl('ajax',array('ac'=>'cunByXiangId'))}", data:{id:id}, dataType:'json',//这个地方是

随机推荐