Swift 3.0 闭包使用及示例(附带Get、Post请求方法)

在swift中,实现回调传值常用的有三种方式:

  • 闭包 closure
  • 代理 delegate
  • 通知 NSNotification

今天我们主要来讲下闭包,swift里面的闭包就相当于OC中的block,只不过swift里面的闭包要比OC中的block更强大,使用更方便

1. 话不多说,咱们先来上个最普通的闭包代码
        //无参无返回值 闭包类型为:() -> Void
        let handler:() -> Void = {
            () in
            print("调用了无参无返回闭包")
        }
        handler()   //调用闭包
        
        //无参无返回简写 闭包类型为:() -> Void (注:可直接省略 () in )
        let simpleHandler:() -> Void = {
            print("调用了--简写--无参无返回闭包")
        }
        simpleHandler()    //调用闭包
        
        //有参有返回 闭包类型为:(Int,Int) -> Int
        let addNumberHandler:(_ a:Int, _ b:Int) -> Int = {
            (a:Int, b:Int) in
            return a + b
        }
        let result = addNumberHandler(10,20) //调用闭包
        print("有参有返回-->返回值为:\(result)")
        
        //有参有返回简写 闭包类型为:(Int,Int) -> Int
        let addNumberSimpleHandler:(Int,Int) -> Int = {
            (a,b) in
            return a + b
        }
        let resultSimple = addNumberSimpleHandler(10,20) //调用闭包
        print("有参有返回-->简写-->返回值:\(resultSimple)")
        
        /** 执行结果值输出:
         调用了无参无返回闭包
         调用了--简写--无参无返回闭包
         有参有返回-->返回值为:30
         有参有返回-->简写-->返回值:30
         */
2. swift中,可以声明一个闭包类型,方便下文引用,用关键字 typealias 类似OC中的 typedef 下面来组示例
//swift中
typealias Handler = (_ name: String, _ age: Int) -> Void
//3.0一般简写为,实现也是,参数会根据上下文推断
typealias Handler = (String,Int) -> Void
//OC中
typedef void(^Handler)(NSString *name, int age);

例:现在在两个不同的类中传值,在OtherViewController定义个闭包属性,ViewController类里实现这个闭包,pop回ViewController之前调用这个闭包从而实现传值,话不多说,上代码

class ViewController: UIViewController

//点击按钮push到OtherViewController页面
@IBAction func PushToOtherViewController(_ sender: UIButton) {
        let otherVC = OtherViewController()
        //实现CallBack闭包属性-->等待调用
        otherVC.CallBack = {
            (name,age) in
            print("我的名字叫\(name),今年\(age)岁了")
        }
        self.navigationController?.pushViewController(otherVC, animated: true)
    }
class OtherViewController: UIViewController

//声明闭包类型
typealias Handler = (String,Int) -> Void
//声明闭包属性
var CallBack:Handler?
//或者 var CallBack:((String,Int) -> Void)?
//点按方法-->pop会ViewController页面,调用闭包传值
@IBAction func popToViewController(_ sender: UIButton) {
        self.CallBack!("超人",24) //调用CallBack闭包
        self.navigationController?.popViewController(animated: true)
    }

当我们Push到OtherViewController页面,点击按钮调用popToViewController方法时,会在ViewController页面打印:我的名字叫超人,今年24岁了

3. 我们来写个简单的网络请求使用闭包,同时讲下尾随闭包和逃逸闭包是怎么回事,咱先看代码

一个简单的get请求,static来修饰,相当于OC中的类方法,请求地址和请求参数分别以字符串的和字典的形式传进方法内进行拼接,拼完发请求,返回结果以调用闭包的形式传出
调用闭包resultBlock: ((_ result:Any?,_ error:Any?) -> Void)?两个参数分别都是可选的并且为任意类型,包括整个闭包也是可选实现的 可简写为:resultBlock: ((Any?,Any?) -> Void)? 直接省略参数名

class HttpManager: NSObject

    //Get请求
    static func getRequestData(urlString: String,paras: Dictionary<String,Any>?,resultBlock: ((_ result:Any?,_ error:Any?) -> Void)?) {
        var i = 0
        var address = urlString
        if let para = paras {
            for (key,value) in para {
                if i == 0 {
                    address += "?\(key)=\(value)"
                }else {
                    address += "&\(key)=\(value)"
                }
                i += 1
            }
        }
        //转换请求URL字符串中的特殊字符
        let request = URLRequest.init(url: URL.init(string: address.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!)!)
        let session = URLSession.shared
        let dataTask = session.dataTask(with: request) { (data, respond, error) in
            if let data = data {
                //系统建议使用的try crash类型,转换JSON失败直接返回nil
                guard let result = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) else {
                    return
                }
                resultBlock!(result,nil) //请求成功,传入result,error传nil
            }else {
                resultBlock!(nil,error) //请求失败,result传nil,传入error
            }
        }
        dataTask.resume()
    }

在ViewController类中调用方法发请求

class ViewController: UIViewController

    /** Get请求测试 */
    func getRequest() {
        //随便用个连接测试下
        let urlStr = "http://yiapi.xinli001.com/v2/yi/ad-list.json?key=18ee0593a01c07f71d28fa936d671457&_platform=ios&_version=3.3.3&name=yiapp-home-ad"
        HttpManager.getRequestData(urlString: urlStr, paras: nil) { (result, error) in
            if let result = result {
                print(result)
            }else {
                print("Get请求失败:\(error)")
            }
        }
    }
  • 尾随闭包: 闭包作为参数追加在方法末尾,这个没什么,无非就是一个方法实现闭包的简写形式
    其实上面的Get请求方法getRequestData(urlString: String,paras: Dictionary<String,Any>?,resultBlock: ((Any?,Any?) -> Void)?)中的resultBlock就是尾随闭包,上面在ViewController类中实现请求就是尾随闭包的简写形式,联想出方法敲回车,系统自动用尾随闭包的形式写出来了
    下面我们来上代码做下对比
        //尾随闭包简写形式
        HttpManager.getRequestData(urlString: urlStr, paras: nil) { (result, error) in
            if let result = result {
                print(result)
            }else {
                print("Get请求失败:\(error)")
            }
        }
        
        //正常书写方式
        HttpManager.getRequestData(urlString: urlStr, paras: nil  , resultBlock:{ (result, error) in
            if let result = result {
                print(result)
            }else {
                print("Get请求失败:\(error)")
            }
        })
4. 逃逸闭包与非逃逸闭包

Swift 的闭包分为 逃逸非逃逸 两种,逃逸闭包(可能)会在函数返回之后才被调用,也就是说,闭包逃离了函数的作用域,而非逃逸闭包则相反,调用闭包要在函数返回之前 (举个简单的例子,网络请求数据,在发请求的时是开启了分线程去请求,主线程还是接着执行下面的代码,很快跑完这个函数方法的作用域,请求到数据的时候,调用闭包返回数据,那么这个闭包必须要逃逸)

逃逸的缺陷: 为了管理内存,一个闭包会强引用它捕获的所有对象,如果你在闭包中访问了当前对象中的任意属性或实例方法,闭包会持有当前对象,因为这些方法和属性都隐性地携带了一个 self参数。这种方式很容易导致,循环引用,这解释了为什么编译器会要求你在闭包中显式地写出对 self 的引用,这迫使你考虑潜在的循环引用。然而,使用非逃逸的闭包不会产生循环引用,编译器可以保证在函数返回时闭包会释放它捕获的所有对象。因此,编译器只要求在逃逸闭包中明确对 self 的强引用

** 逃逸闭包与非逃逸闭包的默认: **

  • 从 Swift 3.0 开始,非逃逸闭包变成了闭包参数的默认形式,如果你想允许一个闭包参数逃逸,需要给闭包形参前面增加一个 @escaping 的标注
  • 在 Swift 3 之前,完全是另外一回事:逃逸是默认状态,你可以添加 @noescape 来覆盖此状态。新的行为更好,因为在默认状态下是安全的:遇到有潜在循环引用的情况时,一个方法调用必须显式地予以标注。因此@escaping 标识符还有警示开发者的作用

** 可选型的闭包总是逃逸的: **
讲到这里我们会奇怪,既然swift3.0以后默认非逃逸闭包,但是上面的网络请求闭包形参前面没加@escaping修饰呢,哈哈哈...标题已经写出来了,可选闭包默认是逃逸闭包,闭包被用作参数,但是当闭包被包裹在其他类型(例如元组、枚举的 case 以及可选型)中的时候,闭包仍旧是逃逸的。由于在这种情况下闭包不再是即时的参数,它会自动变成逃逸闭包。因此,在 Swift 3.0 中,当你编写一个接受函数类型参数的函数时,该参数不能同时是可选型和非逃逸的

那这样吧,我再写个Post请求类方法(简写形式,省略参数名),这样用非可选类型,逃逸闭包标识,与上面的Get请求方法做对比,这样更好理解(两者写的方式不一样,但实质上都是逃逸的)

class HttpManager: NSObject

    //POST请求
    static func postRequestData(urlString:String,paras:Dictionary<String,Any>?,resultBlock: @escaping  (Any?,Any?) -> Void) {
       
        var i = 0
        var body:String = ""
        if let paras = paras {
            for (key,value) in paras {
                if i == 0 {
                    body += "\(key)=\(value)"
                }else {
                    body += "&\(key)=\(value)"
                }
                i += 1
            }
        }
        var request = URLRequest.init(url: URL.init(string: urlString)!)
        request.httpMethod = "POST"
        request.httpBody = body.data(using: .utf8)
        request.allHTTPHeaderFields = ["Content-Type":"application/json"] //有的服务器端可能要加上这个请求头
        let session = URLSession.shared
        let dataTask = session.dataTask(with: request) { (data, respond, error) in
            if let data = data {
                guard let result = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) else {
                    return
                }
                resultBlock(result,nil)  //请求成功
            }else {
                resultBlock(nil,error)  //请求失败
            }
        }
        dataTask.resume()
    }

最后贴上GitHub代码,有需要的点击文字去下载

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

推荐阅读更多精彩内容