iOS开发进阶

Swift 闭包

2016-04-06  本文已影响312人  iOS_成才录

1、闭包简介

闭包和OC中的Block非常相似
        OC中的Block类似于匿名函数
        闭包是用来定义函数
        作用: Block是用于保存一段代码, 在需要的时候执行
             闭包也是用于保存一段代码, 在需要的时候执行
        做一个耗时操作
/*
        OC: block类似于匿名函数, 用于封装代码块, 在特点的时候执行
            执行一些耗时操作
            类型: 返回值类型(^block名称)(形参列表)
            值: 
            ^(形参列表){
                需要执行的代码
            }
        
        Swift: 闭包是用于定义函数(Swift中函数就是闭包, 闭包就是一个特殊的函数)
            执行一些耗时操作
            类型: (形参列表)->返回值类型
            值: 
            {
                (形参列表)->返回值类型
                in 
                需要执行的代码
            } // in 的含义是用于区分形参返回值和执行代码
        */

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self loadData:^{
        NSLog(@"刷新UI");
    }];
}
- (void)loadData:(void(^)())finished
{
//    __weak  : 如果对象释放, 会自动设置为nil
//    __unsafe_unretained: 如果对象释放, 不会自动设置为nil

    // 1.在子线程中加载数据
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@", [NSThread currentThread]);
        NSLog(@"加载数据");
        
        // 2.在主线程中回调, 刷新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"%@", [NSThread currentThread]);
            finished();
        });
    });
}

2、 闭包基本使用

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        
        /*
        闭包的几种格式
        1. 完整写法
        loadData ({ () -> () in
            print("更新UI")
        })
        
        2.如果闭包没有形参, 那么in和in之前的代码都可以省略
        loadData ({
            print("更新UI")
        })
        
        3.如果闭包是函数的最后一个参数, 那么闭包可以写在函数()的后面
        loadData (){
            print("更新UI")
        }
        
        4.如果函数只有一个闭包参数, 那么函数的()可以省略
        loadData {
            print("更新UI")
        }
        */
       
      
        // in是用于区分代码和描述
       loadData { () -> () in 
          print("更新UI")
        }
    }
    
    func loadData(finished: ()->())
    {
        dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
            print(NSThread.currentThread())
            print("加载数据")
            
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                print(NSThread.currentThread())
                
                // 回调通知调用者
                finished()
            })
        }
    }

3、闭包的参数和返回值

       // 1.创建UIScrollview
        let sc = UIScrollView(frame: CGRect(x: 0, y: 200, width: UIScreen.mainScreen().bounds.width, height: 50))
        sc.backgroundColor = UIColor.redColor()
        
        // 2.通过循环创建15个按钮
        let count = 15
        let width:CGFloat = 80
        let height = sc.bounds.height
        for i in 0..<count
        {
            let btn = UIButton()
            btn.setTitle("标题\(i)", forState: UIControlState.Normal)
            btn.frame = CGRect(x: CGFloat(i) * width, y: 0, width: width, height: height)
            btn.backgroundColor = UIColor.greenColor()
            // 3.将按钮添加到UIScrollview上
            sc.addSubview(btn)
        }
        sc.contentSize = CGSize(width: CGFloat(count) *  width, height: height)
        
        // 4.将UIScrollview添加到控制器view上
        view.addSubview(sc)

// 2.1 自定义方法:创建子控件并返回UIScrollView

 // 技巧: 在编写闭包代码时, 不管三七二十一先写上 ()->(), 然后再修改
    /*
       形参1 getNumber: ()->Int 指定添加子控件的个数闭包 返回Int类型;
       形参2 createView: (index: Int)->UIView) 用来根据index索引来创建子控件返回UIView类型闭包;
    
       函数的返回值:UIScrollView类型
    */
    func createScrollview(getNumber: ()->Int, createView: (index: Int)->UIView) -> UIScrollView
    {
        
        // 1.创建UIScrollview
        let sc = UIScrollView(frame: CGRect(x: 0, y: 200, width: UIScreen.mainScreen().bounds.width, height: 50))
        sc.backgroundColor = UIColor.redColor()
        
        // 2.通过循环创建15个按钮
        let count = getNumber()
        let width:CGFloat = 80
        let height = sc.bounds.height
        for i in 0..<count
        {
            /*
            let btn = UIButton()
            btn.setTitle("标题\(i)", forState: UIControlState.Normal)
            btn.frame = CGRect(x: CGFloat(i) * width, y: 0, width: width, height: height)
            btn.backgroundColor = UIColor.greenColor()
            */
            let subView = createView(index: i)
            subView.frame = CGRect(x: CGFloat(i) * width, y: 0, width: width, height: height)
            
            // 3.将按钮添加到UIScrollview上
            sc.addSubview(subView)
        }
        sc.contentSize = CGSize(width: CGFloat(count) *  width, height: height)
        
       return sc
    }

// 2.1 使用自定义方法创建UIScrollView返回并添加到view视图中

override func viewDidLoad() {
        super.viewDidLoad()
        
        // 调用自定义方法:createScrollview(getNumber: ()->Int, createView: (index: Int)->UIView) -> UIScrollView

        let sc = createScrollview({ () -> Int in
            return 20
            }) { (index) -> UIView in
//                let btn = UIButton()
//                btn.setTitle("标题\(index)", forState: UIControlState.Normal)
//                btn.backgroundColor = UIColor.greenColor()
                
                let label = UILabel()
                label.text = "标题\(index)!!!"
                label.backgroundColor = (index % 2 == 0) ? UIColor.greenColor() : UIColor.purpleColor()
                return label
        }
        view.addSubview(sc)
       
    }

3、闭包循环引用

/*
闭包中使用了外部属性self,就对对其进行强引用,同OC中block一样会出现循环引用的问题,如何解决

  OC中如何解决:  __weak typeof(self) weakSelf = self;
  Swift中如何解决: weak var weakSelf = self
  对应关系:  __weak == weak   __unsafe_unretained == unowned

注意:
   __weak  : 如果对象释放, 会自动设置为nil
  __unsafe_unretained: 如果对象释放, 不会自动设置为nil
*/

import UIKit

class ViewController: UIViewController {

    // 在Swift中, 如果在某个类中定义一个属性, 那么这个属性必须要初始化, 否则就会报错
    // 如果占时不想初始化, 那么可以在后面写上一个?号
    
    // 注意: 在设置闭包属性是可选类型时一定更要用一个()括住闭包的所有的类型, 否则只是指定了闭包的返回值是可选的
    // 错误写法: var callback: ()->()?
    var callback: (()->())?  // 定义一个闭包属性
    
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        
        // OC中如何解决:  __weak typeof(self) weakSelf = self;
        // Swift中如何解决: weak var weakSelf = self
        // 对应关系:  __weak == weak   __unsafe_unretained == unowned
        
      // 1、bug:闭包与self会循环引用的问题出现,两者都无法释放掉。
       // 因为:闭包中使用了外部属性self,就对对其进行强引用,同OC中block一样会出现循环引用的问题
        loadData {() -> () in
            print("被回调了")
            
            // 在Swift开发中, 能不写self就不写slef
            // 一般情况下只有需要区分参数, 或者在闭包中使用
           self.view.backgroundColor = UIColor.greenColor()
        }

        // 2、解决方式一:weak解决
        // 2.1 
        weak var weakSelf = self  // 注意:weak修饰的weakSelf时可选的,如果我们使用可选类型数据,必须要强制解包  weakSelf!
        loadData {() -> () in
            print("被回调了")
            weakSelf!.view.backgroundColor = UIColor.greenColor() // 可选数据weakSelf 强制解包  weakSelf!
        }

         // 2.2 
         // [weak self] ,这样说明闭包里面使用的self不会被强引用了。但是是可选类型,所以我们使用self的时候就需要自己手动强制解包 “!” => self!.view.backgroundColor
          loadData {[weak self] () -> () in
            print("被回调了")
            self!.view.backgroundColor = UIColor.greenColor() // 可选数据weakSelf 强制解包  weakSelf!
        }

        // 3、解决方式二:unowned解决
           // 好处:相对weak, [unowned self] 修饰的self不是可选类型,这样就不用像上面weak修饰的self那样自己手动利用"!"强制解包了。
        /*
        loadData { [unowned self] () -> () in
            print("被回调了")
           // 注意:unowned修饰self不是可选类型,一定有值,所以不用手动强制解包了
            self.view.backgroundColor = UIColor.greenColor()
        }
        */
    }
    
    func loadData(finished: ()->())
    {
        callback = finished
        // 1.加载数据
        print("加载数据")
        
        // 2.执行回调
        finished()
    }

    // deinit 相当于OC中的dealloc方法
    // 只要一个对象释放就会调用deinit方法
    deinit
    {
        print("88")
    }
}
上一篇下一篇

猜你喜欢

热点阅读