Swift 闭包

作者: iOS_成才录 | 来源:发表于2016-04-06 14:01 被阅读312次

    1、闭包简介

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

    • 实例:

      • 需求: 在控制器的View上添加一个UIScrollview, 然后在UIScrollview上添加15个按钮, 让按钮平铺
    • 1、不使用闭包简单实现

           // 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、使用闭包封装方法:快速创建
    
    // 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")
        }
    }
    

    相关文章

      网友评论

        本文标题:Swift 闭包

        本文链接:https://www.haomeiwen.com/subject/yuushttx.html