09-closure

闭包

  • 闭包类似于 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已经被释放,则出现野指针访问
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值