• [Swift2.0系列]Defer/Guard 基础语法


    1.Defer

    Swift2.0中加入了defer新语法声明。defer译为延缓、推迟之意。那么在Swift2.0中它将被应用于什么位置呢?比如,读取某目录下的文件内容并处理数据,你需要首先定位到文件目录,打开文件夹,读取文件内容以及处理数据,关闭文件以及文件夹。倘若一切顺利,只需按照设定好的程序流程走一轮即可;不过考虑事情要面面俱到,倘若中间某个环节失败,比如读取文件内容失败、处理数据失败等等,还需要进行一些后续收尾工作,即关闭文件或关闭文件夹(当然就算顺利执行,也是要关闭的)。

    先谈谈defer的基础语法,声明方式如下:

    defer{ // 做一些事情}

    可以看到声明方式非常简单,defer关键字打头,紧跟{}程序块,大括号中添加延迟处理代码。平常应用方式如下:

    func doSomethingWithDefer(){
        // 1
        openDirectory()
        // 2
        defer{closeDirectory()}
        // 3
        openFile()
        // 4
        defer{closeFile()}
    
        // 做其他杂七杂八事情...
    }

    分析代码: 

    1. 定位到目录并打开指定文件夹,倘若打开文件夹失败则结束函数。
    2. 主要到defer的用法,这条语句并不会马上执行,而是被推入栈中,直到函数结束时才再次被调用。
    3. 打开文件,倘若失败则结束函数。
    4. defer内容关闭文件,这条语句一样不会被马上执行,而是推入栈中,此时它位于defer{closeDirectory()}语句的上方,直到函数结束时才再次被调用。

    倘若一切都顺利,函数运行到最后了,开始从栈中依次弹出方才推入的defer语句,首先是closeFile(),其次是closeDirectory()。确实当我们处理完文件,需要先关闭文件,再关闭文件夹。

    现在试想一种情况,我们已经打开文件夹,并且推closeDirectory()到栈中,执行第三步openFile()操作的时候出错了!那么下面所有操作就无法进行下去,结束整个函数了!前文说到函数结束时开始执行defer栈中的内容,关闭文件夹。会有人说怎么不关闭文件,拜托失败了就意味着文件没被打开,何来关闭一说。

    最后必须说下defer的作用域,这点灰常重要。

    注意作用域,其次是调用顺序——即一个作用域结束,该作用域中的defer语句自下而上调用。

    func lookforSomething(name:String) throws{  
      //这里是作用域1 整个函数作用域
    
      print("1-1")
      if name == ""{
        //这里是作用域2 if的作用域
        print("2-1")
        defer{
          print("2-2")
        }
        print("2-3")
      }
      print("1-2")
      defer{
        print("1-3")
      }
      print("1-4")
    
      if name == "hello"{
        //作用域3
        print("3-1")
        defer{
          print("3-2")
        }
        print("3-3")
        defer{
          print("3-4")
        }
      }
    }
    //有兴趣的看看依次输出什么
    //try! lookforSomething("")
    //调出 debug Area 快捷键 shift+ command + y
    try! lookforSomething("hello")

    其实先前有个地方说的不准确,并不是函数结束时开始执行defer栈推出操作,而是每当一个作用域结束就进行该作用域defer执行。

    2.Guard

    guard有控制、警戒之意,语法简单,只需两个示例代码即可明白。

    示例一:

    // 这里使用if 和 guard进行对比 你会懂的更多
    if age < 13 {
        return  //当年龄小于13时 程序返回 不进行之后的操作
    }

    guard改写

    guard age >= 13 else{
    return 
    }

    可以看到代码的意思是保证(guard)age值大于等于13 否则(else)返回,不执行下面程序。

    guard充当了警卫员一职,保证条件满足情况下,才会让你通过,否则只能else让你返回了!切记else中一定需要有返回的语句,比如return、continue、break、throw这种提早退出的关键字!!

    实例二:

    关于if-let解包,我们一般这么写:

    func greet(person: [String: String]) {
        if let name = person["name"]{
            print("Hello (name)!")//名字存在
    
            if let location = person["location"]{
                print("I hope the weather is nice in (location).")
            }else {
                print("I hope the weather is nice near you.")
                return
            } 
        }else {
            return
        }  
    }
    
    greet(["name": "John"])
    // prints "Hello John!"
    // prints "I hope the weather is nice near you."
    greet(["name": "Jane", "location": "Cupertino"])
    // prints "Hello Jane!"
    // prints "I hope the weather is nice in Cupertino."

    使用guard改写之后:

    func greet(person: [String: String]) {
        guard let name = person["name"] else {
            return
        }
    
        print("Hello (name)!")
    
        guard let location = person["location"] else {
            print("I hope the weather is nice near you.")
            return
        }
    
        print("I hope the weather is nice in (location).")
    }
    
    greet(["name": "John"])
    // prints "Hello John!"
    // prints "I hope the weather is nice near you."
    greet(["name": "Jane", "location": "Cupertino"])
    // prints "Hello Jane!"
    // prints "I hope the weather is nice in Cupertino."

    如此改法有点有如下几个方面:

    • 代码清爽直观。
    • 不必再if套if,会混淆你的逻辑。
    • if let name = person["name"]{}其中name的作用域仅局限于紧随的大括号中,而guard let name = person["name"]范围更广!

    实例三:

    出自Thinking in Swift, Part 2: map those arrays一文中例子,感觉不出,供大家学习。代码简单易懂,又不乏知识点。

    class ListItem {
        var icon: UIImage?
        var title: String = ""
        var url: NSURL!
    
        static func listItemsFromJSONData(jsonData: NSData?) -> [ListItem] {
            guard let nonNilJsonData = jsonData,
                let json = try? NSJSONSerialization.JSONObjectWithData(nonNilJsonData, options: []),
                let jsonItems = json as? Array<NSDictionary>
                else {
                    // If we failed to unserialize the JSON or that JSON wasn't an NSArray,
                    // then bail early with an empty array
                    return []
            }
    
            var items = [ListItem]()
            for itemDesc in jsonItems {
                let item = ListItem()
                if let icon = itemDesc["icon"] as? String {
                    item.icon = UIImage(named: icon)
                }
                if let title = itemDesc["title"] as? String {
                    item.title = title
                }
                if let urlString = itemDesc["url"] as? String, let url = NSURL(string: urlString) {
                    item.url = url
                }
                items.append(item)
            }
            return items
        }
    }

    实例四(2015.12.07新增):

    enum CircleAreaCalculationError: ErrorType {
        case RadiusNotSpecified
        case RadiusUnvalid
    
    }
    
    // 尽管还没有成为鞭尸金字塔 但是也快了
    func calculateCirlceArea(radius:Double?) throws -> Double {
    
        if let radius = radius {
            if  radius > 0 {
                return radius*radius*M_PI
            } else {
                throw CircleAreaCalculationError.RadiusUnvalid
            }
        } else {
            throw CircleAreaCalculationError.RadiusNotSpecified
        }   
    }

    使用guard修改之后:

    enum CircleAreaCalculationError: ErrorType {
        case RadiusNotSpecified
        case RadiusUnvalid
    }
    
    func calculateCirleArea(radius:Double?) throws -> Double {
    
        guard let radius = radius else {
            throw CircleAreaCalculationError.RadiusNotSpecified
        }
    
        guard radius > 0 else {
            throw CircleAreaCalculationError.RadiusUnvalid
        }
    
        return radius*radius*M_PI   
    }
  • 相关阅读:
    跟光磊学Python开发-面向对象入门
    插件调用下推操作
    K3Wise老单获取单据行数
    git 添加和删除 global 的 remote.origin.url
    CSV转Excel格式
    java 下载文件
    windows下安装redis并设置自启动
    linxu jdk安装
    Linux安装部署Redis
    CentOS上安装MySQL(使用国内源)
  • 原文地址:https://www.cnblogs.com/Ice-snowPride/p/5332105.html
Copyright © 2020-2023  润新知