一、 基本概念

闭包(Closures)是自包含的功能代码块,可以在代码中使用或者用来作为参数传值。 在Swift中的闭包与C、OC中的blocks和其它编程语言(如C#)中的lambda,javascript中的函数嵌套等类似。 闭包可以捕获和存储上下文中定义的的任何常量和变量的引用。这就是所谓的变量和变量的自封闭, 因此闭包还会处理所有捕获的引用的内存管理。 全局函数和嵌套函数其实就是特殊的闭包。 闭包的形式有:

(1)全局函数都是闭包,有名字但不能捕获任何值。
(2)嵌套函数都是闭包,且有名字,也能捕获封闭函数内的值。
(3)闭包表达式都是无名闭包,使用轻量级语法,可以根据上下文环境捕获值。

Swift中的闭包有很多优化的地方:
(1)根据上下文推断参数和返回值类型
(2)从单行表达式闭包中隐式返回(也就是闭包体只有一行代码,可以省略return)
(3)可以使用简化参数名,如$0,$1(从0开始,表示第i个参数...)

(4)提供了尾随闭包语法(Trailing closure Syntax)


二、使用举例(这里所列举的例子,均从《The Swift Programming Language》这本书总结所得)
下面用Swift标准库中的sort方法来一步步简化闭包写法
sort函数需要两个参数
参数一:数组
参数二:一个闭包:带有两个参数,这两个参数类型与数组中的元素类型相同,返回值是Bool
数组:

var names = ["Swift","Arial","Soga","Donary"]

第一种方式:使用函数
func backwards(firstString: String,secondString: String) -> Bool {  
   return firstString > secondString // 降序排序  
 }
调用:
sort(&names,backwards)

这种方式的使用相当于回调backward方法。


第二种方式:使用闭包方式
完整闭包写法是在花括号内有参数列表和返回值,用关键字in表明闭包体的开始
(1) (firstString: String,secondString: String) 闭包参数列表
(2) -> Bool 指明闭包返回值类型是Bool
(3) in关键字表明闭包体的开始

sort(&names,{ (firstString: String,secondString: String) -> Bool in  
    return firstString > secondString  
    }) 

这里可以进一步简化写法,因为闭包代码比较短,可以写到一行上
sort(&names,secondString: String) -> Bool in return firstString > secondString})

下面再进一步简化写法 :根据环境上下文自动推断出类型,参数列表都没有指明类型,也没有指明返回值类型,这是因为swift可以根据上下文推测出,firstString和secondString的类型会是names数组元素的类型,而返回值类型会根据return语句结果得到
sort(&names,{ firstString,secondString in return firstString > secondString}) 

再进一步简化:隐式返回(单行语句闭包),因为闭包体只有一行代码,可以省略return
sort(&names,secondString in firstString > secondString})

再进一步简化:使用简化参数名($i,i=0,1,2...从0开始的),Swift会推断出闭包需要两个参数,类型与names数组元素相同
sort(&names,{ $0 > $1 })  

最简单的一种写法:使用操作符
sort(&names,>)

三、 尾随闭包(Trailing Closures)
如果函数需要一个闭包参数作为参数,且这个参数是最后一个参数,而这个闭包表达式又很长时, 使用尾随闭包是很有用的。尾随闭包可以放在函数参数列表外,也就是括号外。就是将原本在参数列表内的闭包提取到函数的后边书写,这样就利于阅读及使用。
1. 还是用sort的方法为例,正常的闭包写法如下:

sort(&names,secondString: String) -> Bool in  
    return firstString > secondString  
    }) 

但可以发现,sort函数中,第二个参数非常的长并且不利于阅读,这时,我们可以使用尾随闭包进行改造,代码如下:
sort(&names){
    (firstString,secondString) -> Bool in
    return firstString > secondString
}

2. map方法使用举例,输出一个数组对应的字符串
var numbers = [1,2,3]
let strings = numbers.map({
    (var number) -> String in
    var output = ""
    while number > 0 {
        output = String(number % 10) + output
        number /= 10
    }
    return output
})

注意到,map是一个方法,而其参数就只有一个闭包,所以我们同样可以使用尾随闭包的形式,写成如下形式
var numbers = [1,3]
let strings = numbers.map(){
    (var number) -> String in
    var output = ""
    while number > 0 {
        output = String(number % 10) + output
        number /= 10
    }
    return output
}

而map函数没有任何其他的参数,只有一个闭包参数,所以map后面的"()"也可以省略,最终的尾随闭包可以写成如下形式
var numbers = [1,3]
let strings = numbers.map{
    (var number) -> String in
    var output = ""
    while number > 0 {
        output = String(number % 10) + output
        number /= 10
    }
    return output
}

四、 捕获值
闭包可以根据环境上下文捕获到定义的常量和变量。闭包可以引用和修改这些捕获到的常量和变量。在Swift中闭包的最简单形式是嵌套函数。

func increment(#amount: Int) -> (() -> Int) {  
  var total = 0  
  func incrementAmount() -> Int {  
    total += amount // total是外部函数体内的变量,这里是可以捕获到的  
    return total  
  }  
  return incrementAmount // 返回的是一个嵌套函数(闭包)  
}

闭包是引用类型,所以incrementByTen声明为常量也可以修改total
let incrementByTen = increment(amount: 10)   
incrementByTen() // return 10,incrementByTen是一个闭包  
// 这里是没有改变对increment的引用,所以会保存之前的值  
incrementByTen() // return 20     
incrementByTen() // return 30     
  
let incrementByOne = increment(amount: 1)  
incrementByOne() // return 1  
incrementByOne() // return 2      
incrementByTen() // return 40   
incrementByOne() // return 3  

五、 闭包的循环引用问题
在objective-c时期,在使用block的时候不得不考虑block的循环引用问题。当时采取的措施就是将循环引用的一方弱化,比如:

__weak typeof(self) wSelf = self;

在Swift中的闭包中也存在同样的问题,其实大致思路是一致的,我们可以使用uNowned 关键字。
Example, 将一个对象的属性转化为XML形式显示:

class HTMLElement {
    var name: String
    var text: String?
    
    init(name: String,text: String?) {
        self.name = name
        self.text = text
    }
    
    lazy var asHtml: () -> String = {
        [uNowned self] in
        if let text = self.text {
            return "<\(self.name)>\(self.text)</\(self.name)>"
        } else {
            return "<\(self.name)>"
        }
    }
}


var html = HTMLElement(name: "Node",text: "Jack")
html.asHtml()

上面的代码中,self对asHtml这个闭包属性有强引用,而asHtml内部又有对self的强引用,所以我们可以使用[uNowned self] 将self “弱化”。从而解除了循环引用。


六、 闭包在UIKit实际环境中的使用举例
UI界面如下:


功能很简单
1. 上面灰色的View就是自定义的View(TestView.swift),它包含一个文本输入框和一个“GO”按钮。
2. 后面这一块就是控制器的View(ViewController.swift),它里面包含一个testLabel。
3. 点击"GO"按钮后将文本框中的内容回调到ViewController.swift中的Label显示
TestView.swift 代码:

class TestView: UIView {


    private weak var textField1: UITextField!
    
    var testClosure: ((str1: String) -> Void)?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        var size = UIScreen.mainScreen().bounds.size
        self.frame = CGRectMake(0,size.width,kViewHeight)
        self.backgroundColor = UIColor.grayColor()
        
        var textField1 = UITextField(frame: CGRectMake(10,30,150,30))
        textField1.backgroundColor = UIColor.whiteColor()
        self.addSubview(textField1)
        self.textField1 = textField1
        
        var btn = UIButton(frame: CGRectMake(300,50,30))
        btn.setTitle("GO",forState: UIControlState.normal)
        btn.addTarget(self,action: "showResult",forControlEvents: UIControlEvents.TouchDown)
        self.addSubview(btn)
    }
    
    func showResult() {
        self.testClosure?(str1: self.textField1.text)
    }
    
    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

代码中,我定义了一个testClosure闭包属性,当点击“GO”按钮则会调用其方法。注意到闭包的定义
var testClosure: ((str1: String) -> Void)?
它是可空的,因为在控制器ViewController.swift中不一定完成了对testClosure闭包属性赋值的工作。所以在调用闭包的时候写成
self.testClosure?(str1: self.textField1.text)
而在ViewController.swift赋值的代码如下:
class ViewController: UIViewController {
    
    @IBOutlet weak var testLabel: UILabel!


    override func viewDidLoad() {
        super.viewDidLoad()
        
        var tab = TestView()
        tab.testClosure = {
            [uNowned self] // 去除循环引用
            (str1: String) -> Void in
            self.testLabel.text = "First Record:\(str1)"
        }
        self.view.addSubview(tab)
    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // dispose of any resources that can be recreated.
    }
}

Swift:闭包(Closures)的更多相关文章

  1. HTML5 WebSocket实现点对点聊天的示例代码

    这篇文章主要介绍了HTML5 WebSocket实现点对点聊天的示例代码的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. ios – 在Swift的UIView中找到UILabel

    我正在尝试在我的UIViewControllers的超级视图中找到我的UILabels.这是我的代码:这是在Objective-C中推荐的方式,但是在Swift中我只得到UIViews和CALayer.我肯定在提供给这个方法的视图中有UILabel.我错过了什么?我的UIViewController中的调用:解决方法使用函数式编程概念可以更轻松地实现这一目标.

  3. ios – 在Swift中将输入字段字符串转换为Int

    所以我非常擅长制作APP广告Swift,我试图在文本字段中做一些非常简单的输入,取值,然后将它们用作Int进行某些计算.但是’vardistance’有些东西不正确它是导致错误的最后一行代码.它说致命错误:无法解开Optional.None解决方法在你的例子中,距离是一个Int?否则称为可选的Int..toInt()返回Int?因为从String到Int的转换可能失败.请参阅以下示例:

  4. 如何在iOS中检测文本(字符串)语言?

    例如,给定以下字符串:我想检测每个声明的字符串中使用的语言.让我们假设已实现函数的签名是:如果没有检测到语言,则返回可选字符串.因此,适当的结果将是:有一个简单的方法来实现它吗?

  5. xamarin – 崩溃在AccountStore.Create().保存(e.Account,“);

    在Xamarin.Forms示例TodoAwsAuth中https://developer.xamarin.com/guides/xamarin-forms/web-services/authentication/oauth/成功登录后,在aOnAuthenticationCompleted事件中,应用程序在尝试保存到Xamarin.Auth时崩溃错误说不能对钥匙串说期待着寻求帮助.解决方法看看你

  6. ios – 将视频分享到Facebook

    我正在编写一个简单的测试应用程序,用于将视频从iOS上传到Facebook.由于FacebookSDK的所有文档都在Objective-C中,因此我发现很难在线找到有关如何使用Swift执行此操作的示例/教程.到目前为止我有这个在我的UI上放置一个共享按钮,但它看起来已禁用,从我读到的这是因为没有内容设置,但我看不出这是怎么可能的.我的getVideoURL()函数返回一个NSURL,它肯定包含视

  7. xcode – 错误“线程1:断点2.1”

    我正在研究RESTAPI管理器.这是一个错误,我无法解决它.我得到的错误在下面突出显示.当我打电话给这个班级获取资源时:我评论的线打印:Thread1:breakpoint2.1我需要修复错误的建议.任何建议都非常感谢解决方法您可能在不注意的情况下意外设置了断点.单击并拖动代表断路器外部断点的蓝色刻度线以将其擦除.

  8. ios – 更改导航栏标题swift中的字符间距

    类型的值有人可以帮我这个或建议一种不同的方式来改变swift中导航栏标题中的字符间距吗?解决方法您无法直接设置属性字符串.你可以通过替换titleView来做一个技巧

  9. ios – 如何从变量访问属性或方法?

    是否可以使用变量作为Swift中方法或属性的名称来访问方法或属性?在PHP中,您可以使用$object->{$variable}.例如编辑:这是我正在使用的实际代码:解决方法你可以做到,但不能使用“纯粹的”Swift.Swift的重点是防止这种危险的动态属性访问.你必须使用Cocoa的Key-ValueCoding功能:非常方便,它完全穿过你要穿过的字符串到属性名称的桥,但要注意:这里是龙.

  10. ios – Swift中的UIView动画不起作用,错误的参数错误

    我正在尝试制作动画并使用下面的代码.我得到“无法使用类型’的参数列表调用’animateWithDuration'(FloatLiteralConvertible,延迟:FloatLiteralConvertible,选项:UIViewAnimationoptions,动画:()–>()–>$T4,完成:(Bool)–>(Bool)–>$T5)’“错误.这意味着我使用了错误的参数.我错了.请

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部