09-closure

来源:互联网 发布:域名指向两个ip 编辑:程序博客网 时间:2024/06/06 03:46

闭包

  • 闭包类似于 OC 中的 Block
    • 预先定义好的代码
    • 在需要时执行
    • 可以当作参数传递
    • 可以有返回值
    • 包含 self 时需要注意循环引用

定义

  • 定义一个函数
//: 定义一个 sum 函数func sum(num1 num1: Int, num2: Int) -> Int {    return num1 + num2}sum(num1: 10, num2: 30)//: 在 Swift 中函数本身就可以当作参数被定义和传递let mySum = sumlet 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 = selfloadData {    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
原创粉丝点击