本文主要介绍Swift的闭包的使用并与OC的Block做比较。学习Swift是绕不过闭包的,因为无论是全局函数还是嵌套函数都是闭包的一种,本文主要介绍闭包表达式。
1.闭包表达式的使用
// 1.定义一个闭包
let myClosure = {
(s1: String, s2:String) -> Bool
in
self.count = 10;
print("------");
return s1 > s2
}
print(count!);
// 2.调用闭包
let result = myClosure("Chris","Alex")
print("result = \(result)")
print("count = \(count!)")
日志
result = true
count = 10
总结 :1.和oc的block的声明和调用在形式上是极其类似的,不过闭包可以直接修改局部变量和全局变量的值,而block需要__block 关键字。
// 3.Tralling 闭包(尾随)
let digitNames = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]
let strings = numbers.map {
( number) -> String in
var number = number
var output = ""
while number > 0 {
output = digitNames[number % 10]! + output
number /= 10
}
return output
}
print(strings);
日志
["OneSix", "FiveEight", "FiveOneZero"]
2.Trailing 闭包
函数的表现形式:(void)函数名(参数)。如果一个函数的最后一个参数是一个闭包,允许你写在参数所在哪个()外面。
(void)函数名(参数) {
};
如果只有闭包一个参数,括号可以省略。变成:
(void)函数名 {
};
当闭包里的代码很多的时候,这样写可以增加代码的可读性,多用于调用系统的函数。
举例之前先介绍一下map函数,map属于Array的一个函数,调用这个函数需要传入一个闭包,返回一个新数组。Array里的每一个元素都会调用这个闭包,生成一个新对象,加入到新数组中。相当于 自动执行了for in和addobject两个方法,很实用。
let digitNames = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]
使用Trailing 闭包之前
let strings = numbers.map({
( number) -> String in
var number = number
var output = ""
while number > 0 {
output = digitNames[number % 10]! + output
number /= 10
}
return output
})
使用Trailing 闭包之后
let strings = numbers.map{
( number) -> String in
var number = number
var output = ""
while number > 0 {
output = digitNames[number % 10]! + output
number /= 10
}
return output
}
就一个参数不明显, 但是参数多了还是很有用的。因为闭包里的代码一般有很多,会导致包含参数的()距离太远。
3. 捕获: 解决嵌套函数的循环引用
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() -> Int {
// 只能捕获包含他的函数体内的变量或常量的值,建立一个副本,相当于深拷贝
// 新变量
runningTotal += amount
return runningTotal
}
print(" ----- runningTotal = \(runningTotal)")
return incrementor
}
let incrementByTen = makeIncrementor(forIncrement: 10)
print("incrementor = \(incrementByTen())")
print("incrementor = \(incrementByTen())")
print("incrementor = \(incrementByTen())")
打印
----- runningTotal = 0
incrementor = 10
incrementor = 20
incrementor = 30
总结:incrementor里的runningTotal就是对:incrementor外的runningTotal的捕获。捕获有以下几个特点。
(1)捕获会生成一个新变量,和捕获变量的值相等,但内存不同,是引用类型,相当于OC中的深拷贝,新变量变化和捕获的变量没有任何关系了。因为incrementor外的runningTotal的值一直没有改变。
(2)嵌套函数对新变量是强引用,只要嵌套函数还在,新变量就在,因为incrementor的返回值是一直增加的。
(3)如果不这样为什么会造成循环引用,incrementor对makeIncrementor变量runningTotal的引用就是对makeIncrementor的引用。运用捕获,就只是对runningTotal值的拷贝,不引用。
4.闭包传值
在OC中我们用block最多的地方就是传值了,同样闭包也是。不过运用block和闭包传值最好是当对象只有一个状态的时候,如果对象状态很多最好用代理。
CycleScrollView 往CycleScrollViewViewController进行传值
import UIKit
// 定义闭包类型
typealias DidSelectItemClosureType = (Int) -> Void
class CycleScrollView: UIView, UICollectionViewDelegate,UICollectionViewDataSource {
// Mark:UICollectionViewDelegate
// 点击方法
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if self.didSelectItemClosure != nil {
// 13.利用闭包传值
self.didSelectItemClosure!(indexPath.row == 3 ? 0 : indexPath.row);
}
}
}
接收闭包传过来的值
import UIKit
class CycleScrollViewViewController: UIViewController {
var cycleScrollView : CycleScrollView?
override func viewDidLoad() {
super.viewDidLoad()
createUI()
}
func createUI() {
self.automaticallyAdjustsScrollViewInsets = false;
cycleScrollView = CycleScrollView.init(frame: CGRect(x:0,y:64,width:ScreenWidth,height:ScreenHeight - 64))
// 闭包传值
cycleScrollView?.didSelectItemClosure = {
(index : Int) -> Void in
print("您点击了第 \(index) 个")
}
self.view.addSubview(cycleScrollView!)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
如果传值的例子有点没看懂,可以去下载我的DEMO,里面有详细的代码。闭包还是很厉害的,不需要我们进行任何操作就解决了循环引用问题,不像block还得对变量进行弱引用。本文部分内容是对Swift闭包详解的整理。