Swift 闭包

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")
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容