Swift中的闭包

  1、闭包的概念

  一门计算机语言如果要支持闭包,必须要有两个前提:支持函数类型,也就是说可以将函数作为参数进行传递,或者能够将函数作为返回值;支持函数嵌套。闭包是一种自包含的匿名函数代码块,它可以作为表达式、函数参数或者函数返回值。闭包表达式的运算结果是一种函数类型。闭包表达式的标准语法格式为:

// 闭包表达式的标准格式
{(参数列表) -> 返回值类型 in
    语句组
}

// 闭包形式
{(a: int, b: Int) -> Int in
    return a + b
}

  闭包表达式的参数列表和函数的参数列表形式一样,其返回值类型也和函数的返回值类型相似。闭包与函数比较明显的一个差别是,返回值类型后面多了一个关键字in。我们知道在Swift中,编译器可以做的事情有很多,比如说类型推断。下面我们就利用编译器的类型推断对上面的闭包形式进行简化。

  2、利用类型推断对闭包进行简化

  在Swift中,编译器可以根据上下文的环境推断出参数和返回值的类型,比如说像上面那个闭包标准形式,完全可以利用编译器特性将其简化为:

// 闭包形式
{(a, b) in
    return a + b
}

// 省略参数列表小括号,并且将其写成一行
{a, b in return a + b}

  3、隐藏return关键字

  如果闭包内部语句组中只有一条语句,比如说像上面的return a + b,那么我们就可以将返回语句中的return关键字给省略掉:

// 省略返回语句中的关键字return
{a, b in a + b}

  需要特别强调一下,只有当关键字in后面的语句组只有一条返回语句时,你才能将关键字return给省略掉。如果关键字in后面有多条语句,那么关键字return则不能省略。比如说,像{a, b in var c = 0; a + b}这种关键字in后面有多条语句的情况,省略return就是错误的写法。

  4、省略参数名

  上面的闭包已经非常简洁了,但是还不是最简洁的。其实还可以再极端一点,将参数名也给省略掉。我们可以用$0、$1、$3...$n来指定闭包中的参数,其中$0表示闭包参数列表中的第一个参数,$1表示闭包参数列表中的第2个参数...依此类推。除了参数列表可以省略之外,关键字in也可以省略,上面的闭包可以简写为:

{$0 + $1}

  像上面这种形式,我之前在《Swift中常用的数据结构(上)》中讲Dictionary时就已经用过:

// 初始化一个字典
var provences = ["SH" : "Shanghai", "BJ" : "Beijing", "HB" : "Hubei", "HN" : "Hunan", "GD" : "Guangdong"]

// 只遍历字典中的键
for provenceAbbr in provences.keys {
    print("字典中各省份的简称是:\(provenceAbbr)")
}

// 对字典中的键值对进行重新排序,然后返回一个数组
let sortedArrFromDict = provences.sorted(by: {$0.0 < $1.0})

// 遍历数组
for (key) in sortedArrFromDict.map({$0.0}) {
    print("字典中的键分别为:\(key)")
}

  5、使用闭包作为返回值

  闭包表达式本质上是函数类型,函数里面有函数返回值,那么闭包里面肯定也有闭包返回值。比如说,我们举一个例子:

// 声明一个整型变量,用于接收闭包返回值
let num1: Int = {(a: Int, b: Int) -> Int in
                    return a + b
                 }(10, 5)
print(num1)  // 结果为15

let num2: Int = {$0 - $1}(10, 5)
print(num2)  // 结果为5

  在上面的代码中,num1为Int类型,因此我们不能直接将闭包表达式赋值给它。但是这个闭包是有返回值的,而且它的返回值刚好也是Int类型,这就需要我们在闭包末尾大括号后面再跟上一对小括号,通过这个小括号为闭包传递参数。需要注意,这种写法在开发过程中拥有广泛的实际应用,尤其是在对控件进行懒加载时,用得非常的多!就以我们在《Swift基础知识补充(二)》中讲值绑定时那个下载网络图片的代码为示例,我们用闭包对它进行改写:

// 对imageView控件进行懒加载
fileprivate lazy var imageView: UIImageView = {

    // 创建imageView控件
    let imageView = UIImageView()
    
    // 设置imageView控件的尺寸
    imageView.frame = CGRect(x: 0, y: 0, width: 200, height: 300)
    
    // 将imageView控件返回
    return imageView
}()

override func viewDidLoad() {
    super.viewDidLoad()
    
    // 先校验图片url地址是否正确
    guard let url = URL(string: "https://gss0.baidu.com/-fo3dSag_xI4khGko9WTAnF6hhy/zhidao/pic/item/0b55b319ebc4b745359202e2c8fc1e178a82153b.jpg") else {
        return
    }
    
    // 再校验图片有没有下载成功
    guard let imageData = try? Data(contentsOf: url) else {
        return
    }
    
    // 将图片控件添加到父控件中
    view.addSubview(imageView)

    // 设置imageView的位置
    imageView.center = view.center

    // 设置imageView的图片
    imageView.image = UIImage(data: imageData)
}

  出于性能方面的考虑,有很多只需要设置一次的控件属性代码,都会放在懒加载后面的闭包中进行设置,所以这种语法一定要掌握。

  6、尾随闭包

  闭包表达式不仅可以作为函数的返回值进行传递,它还可以作为函数的参数进行传递。但是,如果闭包表达式很长,就会导致整个函数的参数列表也非常的长,这样很容易影响程序的阅读性。不过,好在Swift函数支持尾随闭包。我们先来看一个具体的示例,然后再来解释什么是尾随闭包:

// 尾随闭包
func calculate(opt: String, closureP: (Int, Int) -> Int) {
    
    switch opt {
    case "+":
        print("10 + 5 = \(closureP(10, 5))")
    default:
        print("10 - 5 = \(closureP(10, 5))")
    }
}

// 函数调用1:调用的时候将闭包作为参数传递给函数
calculate(opt: "+", closureP: {(a: Int, b: Int) -> Int in return a + b})

// 函数调用2:调用的时候将闭包表达式移到函数小括号后面
calculate(opt: "+") { (a: Int, b: Int) -> Int in
    return a + b
}

// 函数调用3:对函数调用2进行简化
calculate(opt: "+"){$0 + $1}

  现在我们来解释一下上面的代码,看看到底什么叫做尾随闭包。我们定义了一个calculate()函数,这个函数有两个参数,第一个参数opt是String类型,第二个参数closureP是一个闭包类型。第一次调用calculate()函数时,我们是按照正常调用函数的步骤,将opt和closureP这两个参数分别传入。但是,在第二次调用calculate()函数时,我们只给它传递了第一个参数opt,而第二个参数closureP是直接挪到了calculate()函数小括号的外面,像这种写法就是尾随闭包

  尾随闭包是指一个书写在函数小括号后面的闭包表达式,Swift函数支持将其作为最后一个参数进行调用。不过,需要注意的是,在使用尾随闭包时,闭包必须是函数参数列表中的最后一个参数,否则就不能使用尾随闭包的形式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,825评论 4 377
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,887评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,425评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,801评论 0 224
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,252评论 3 299
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,089评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,216评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 31,005评论 0 215
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,747评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,883评论 2 255
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,354评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,694评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,406评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,222评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,242评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,017评论 2 281

推荐阅读更多精彩内容