闭包
- 闭包类似于 OC 中的 Block
- 预先定义好的代码
- 在需要时执行
- 可以当作参数传递
- 可以有返回值
- 包含
self
时需要注意循环引用
定义
- 定义一个函数
//: 定义一个 sum 函数
func sum(num1 num1: Int, num2: Int) -> Int {
return num1 + num2
}
sum(num1: 10, num2: 30)
//: 在 Swift 中函数本身就可以当作参数被定义和传递
let mySum = sum
let result = mySum(num1: 20, num2: 30)
- 定义一个闭包
- 闭包: 闭包类型 = { (行参) -> 返回值 in // 代码实现 }
in
用于区分函数定义和代码实现
//: 闭包: 闭包类型 = { (行参) -> 返回值 in // 代码实现 }
let sumFunc: (num1: Int, num2: Int) -> Int = { (x, y) -> Int in
return x + y
}
sumFunc(num1: 10, num2: 50)
闭包实战
GCD 异步
- 模拟在后台线程加载数据
func loadData() {
dispatch_async(dispatch_get_global_queue(0, 0), { () -> Void in
print("耗时操作 \(NSThread .currentThread())")
})
}
- 尾随闭包,如果闭包是最后一个参数,可以用以下写法
- 注意上下两段代码,
}
的位置
func loadData() {
dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
print("耗时操作 \(NSThread .currentThread())")
}
}
- 闭包的简写,如果闭包中没有参数和返回值,可以省略
func loadData() {
dispatch_async(dispatch_get_global_queue(0, 0)) {
print("耗时操作 \(NSThread .currentThread())")
}
}
自定义闭包参数,实现主线程回调
- 添加没有参数,没有返回值的闭包
override func viewDidLoad() {
super.viewDidLoad()
loadData {
print("完成了 \(NSThread .currentThread())")
}
}
func loadData(finished: () -> ()) {
dispatch_async(dispatch_get_global_queue(0, 0)) {
print("耗时操作 \(NSThread .currentThread())")
dispatch_async(dispatch_get_main_queue()) {
// 执行回调
finished()
}
}
}
- 添加回调参数
override func viewDidLoad() {
super.viewDidLoad()
loadData { message in
print(message + " \(NSThread .currentThread())")
}
}
func loadData(finished: (msg: String) -> ()) {
dispatch_async(dispatch_get_global_queue(0, 0)) {
print("耗时操作 \(NSThread .currentThread())")
dispatch_async(dispatch_get_main_queue()) {
// 执行回调
finished(msg: "over")
}
}
}
闭包的返回值演练
目标:用闭包实现
网易新闻
中滚动标签视图
- 实现
scrollView
函数,返回UIScrollView
func scrollView(frame: CGRect, numbersOfLabel: ()-> Int, labelForIndex: (index: Int) -> UILabel) -> UIScrollView {
// 1. 实例化 scrollView
let sv = UIScrollView(frame: frame)
// 2. label 的数量
let count = numbersOfLabel()
print(count)
// 3. 循环创建 label 的内容
let margin: CGFloat = 8
var x = margin
for i in 0..<count {
let label = labelForIndex(index: i)
label.frame = CGRect(x: x, y: 0, width: label.bounds.width, height: frame.height)
sv.addSubview(label)
// 累加 x 坐标
x += label.bounds.width
}
// 4. 设置 scrollView 的 contentSize
sv.contentSize = CGSize(width: x, height: frame.height)
// 5. 返回
return sv
}
- 函数调用
override func viewDidLoad() {
super.viewDidLoad()
let size = UIScreen.mainScreen().bounds.size
let rect = CGRect(x: 0, y: 20, width: size.width, height: 44)
view.addSubview(scrollView(rect, numbersOfLabel: { () -> Int in
return 10
}) { (index) -> UILabel in
// 1. 实例化 label
let label = UILabel()
// 2. 设置文本内容
label.text = "Hello \(index)"
// 2.1 设置字体 & 对齐方式
label.font = UIFont.systemFontOfSize(18)
label.textAlignment = NSTextAlignment.Center
// 2.2 自动设置大小
label.sizeToFit()
// 2.3 缩小字体
label.font = UIFont.systemFontOfSize(14)
return label
})
}
本演练不仅演示了闭包返回值的应用,同时也模拟了
TableView
的数据源方法用闭包的实现思路
- 自定义视图
class ChannelView: UIScrollView {
init(frame: CGRect, numbersOfLabel: ()-> Int, labelForIndex: (index: Int) -> UILabel) {
// 1. 实例化 scrollView
super.init(frame: frame)
// 2. label 的数量
let count = numbersOfLabel()
print(count)
// 3. 循环创建 label 的内容
let margin: CGFloat = 8
var x = margin
for i in 0..<count {
let label = labelForIndex(index: i)
label.frame = CGRect(x: x, y: 0, width: label.bounds.width, height: frame.height)
addSubview(label)
// 累加 x 坐标
x += label.bounds.width
}
// 4. 设置 scrollView 的 contentSize
contentSize = CGSize(width: x, height: frame.height)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
闭包的循环引用
- 代码准备
// 完成回调属性,控制器对闭包强引用
var finishedBlock: (() -> ())?
private func loadData(finished: () -> ()) {
// 记录闭包
finishedBlock = finished
working()
}
private func working() {
// 如果属性不存在,使用 ! 会崩溃
// finishedBlock!()
// 如果属性不存在,就不执行
finishedBlock?()
}
- 调用闭包,与 Block 一致,闭包会对
self
做一次 copy,从而进行强引用
loadData {
print("\(self.view)")
}
- 测试视图控制器的释放
/// 与 OC 中的 dealloc 类似,注意此函数没有()
deinit {
print("\(classForCoder) 88")
}
解除循环引用
- 与 OC 类似的方法
weak var weakSelf = self
loadData {
print("\(weakSelf?.view)")
}
- Swift 推荐的方法
loadData { [weak self] in
print("\(self?.view)")
}
- 还可以
loadData { [unowned self] in
print("\(self.view)")
}
闭包(Block) 的循环引用小结
Swift
[weak self]
self
是可选项,如果self已经被释放,则为nil
[unowned self]
self
不是可选项,如果self已经被释放,则出现野指针访问
Objc
__weak typeof(self) weakSelf;
- 如果
self
已经被释放,则为nil
- 如果
__unsafe_unretained typeof(self) weakSelf;
- 如果
self
已经被释放,则出现野指针访问
- 如果