美文网首页iOS技术点程序员iOS Developer
Swift中一些常见的关键字一(inout,defer,thro

Swift中一些常见的关键字一(inout,defer,thro

作者: BennyLoo | 来源:发表于2018-01-04 21:00 被阅读342次
    Swift关键字

    Swift从1.0开始一直迭代到如今的4.0,出现了很多关键字。这些关键字,有的帮助我们处理了一些复杂的代码结构设计,有的帮助我们简化了许多代码。这帮助我们提高了开发的效率和降低了维护成本,所以我们做代码优化的时候,应该优先考虑到使用合适的关键字。Swift中各种关键字很多,这篇文章,就一些常用的关键字做一点介绍。

    本文将 inout,defer,throws,rethrows这几个关键字进行介绍。

    inout

    inout关键字用一句话概括:将值类型的对象用引用的方式传递。
    我们经常说值类型引用类型实际上就是指对象的传递方式分别是 按值传递按址传递
    值类型 按值传递:

    let a: Int = 5
    var b: Int = a
    b = b+1 
    print(a,b)    //    5,6
    

    引用类型(class对象) 按址传递

    let p1 = Person()
    P1.name = "name1"
    let p2 = p1
    p2.name = "name2"
    print(p1.name,p2.name)   // name2, name2
    

    inout关键字则可以使得值类型的对象和引用类型的对象一样,以按址传递的方式进行操作,这里的操作不仅包含上述例子中的赋值操作,也包含了函数的参数传递行为。
    通俗的举个例子:我们在使用函数传递一个Int类型对象的时候,通常会将这个对象的值传递进去了。但是如果使用inout修饰对象的类型,则可以将变量的地址传入函数。就像下面这个handle函数一样。

    func test(){
        var a:Int = 5
        handle(a:&a)   //  注意这里使用了取址操作
        print(a)    // 6  
    }
    
    func handle(a:  inout Int){
        print(a)   
        a = a + 1     //如果没有inout修饰的话,这句代码将会报错,主要意思是不能改变一个let修饰的常量。
    }
    

    最终,我们在test 函数中打印的变量a的值被改变了。
    除了 Int类型,诸如:CGFloat,Bool,Character,Array,struct等,这些值类型的对象都可以使用inout修饰,达到使用引用的方式传递的目的。

    defer

    defer关键字用一句话概括:修饰一段函数内任一段代码,使其必须在函数中的其余代码都执行完毕,函数即将结束前调用。也可以理解成将延迟执行。
    为了更好的了解defer的作用,我写了一段代码:

    func test(){
       print("函数开始")
       defer{
           print("执行defer1")
       }
       print("函数将结束")
       defer{
           print("执行defer2")
       }
    }
    
    test()
    打印   //   
    函数开始
    函数将结束
    执行defer2
    执行defer1
    

    我在test函数中,添加了两个 defer,分别打印了两条语句。从下面的打印中,可以看到,两个defer都执行了。并且他们都在函数结束之后执行的:defer总在函数要结束之前调用,尽管我将它们放在了程序较靠前的位置
    defer2先于defer1执行,说明如果一个函数中包含数个defer 的话:程序会按照自下而上的顺序执行defer
    以上是在一个同步的代码中运行的defer,那如果是异步执行的代码defer的作用又是怎样的呢?

        func test(){
            print("函数开始")
            defer{
                print("执行defer1")
            }
            defer{
                print("执行defer2")
            }
            DispatchQueue.main.asyncAfter(deadline: .now()+1) {
                print("异步执行完毕")
            }
            print("函数将结束")
        }
    
    test()
    打印:
    函数开始
    函数将结束
    执行defer2
    执行defer1
    异步执行完毕
    

    异步代码的执行,不会影响defer的执行时间。事实上,defer的执行只和其所在的作用域有关,如果作用域即将被回收,那么会在回收之前执行defer

    throws

    Swift要求的严格的类型安全,我想它对于错误的处理也不太可能马虎吧。回想在OC中,使用NSError处理错误,而实际上NSError处理的错误有限,我们不太可能在日常所有的开发中使用NSError,通常,对于一个需要进行错误处理的时候,我们习惯性使用一个 nil作为参数,就像这样:

        [[NSFileManager defaultManager] contentsOfDirectoryAtURL:@"aURL" 
    includingPropertiesForKeys:@"aKey" options:nil error:nil];
    

    因为类似的错误出现的情况是很极端的场景,大多数时候使用nil是没有问题的。但是,这是一个隐患,不仅不能回馈错误,还增加了维护的成本:至少我得知道这个函数究竟在哪些极端情况下才会返回错误! 而这一切是因为程序员的懒或者马虎造成的,Swift决定从技术上解决这个问题:使用throw可以让程序员必须处理相关的异常代码!
    Swift中提供了Error协议,我们在开发中,如果要自定义自己的错误类型,一般会使用一个Enum来继承Error协议,目的是享用Error已经包含的一些特性。
    下面是一个例子:

    //错误类型枚举
    enum MyError : Error {
        case ErrorOne
        case ErrorTwo
        case ErrorThree
        case ErrorOther
    }
    
    func willThrow(_ type:NSInteger)throws -> NSString{
            print("开始处理错误")
            if type == 1 {
                throw MyError.ErrorTwo
            }else if type == 2 {
                throw MyError.ErrorTwo
            }else if type == 3{
                throw MyError.ErrorThree
            }else {
                throw MyError.ErrorOther
            }
            return "一切都很好"
        }
        
    //调用
         do {
            let str = try self.willThrow(2)
                //以下是非错误时的代码
                print(str) //如果有错误出现,这里将不会执行
            }catch let err as MyError{
                print(err)
            }catch{
                //这里必须要携带一个空的catch 不然会报错。 原因是可能遗漏
          }
        打印//
        ErrorTwo
    

    throws的使用很简单,只需要在可能出现异常的函数或者方法后面添加throws。
    经过这个关键字修饰的函数,在调用的时候,需要程序员加上do-catch来调用。
    对于错误类型开发者来说,只需要使用Throws进行修饰,就保证了以后的调用者必然需要对相应的错误进行处理(当然也可以不处理,但无论如何,错误被throw携带出来了,以后的维护和优化不需要重新做错误处理的设计,直接加上错误处理的逻辑即可)。

    rethrows

    rethrows是异常往上传递的关键字。上面说了throws用在可能出现异常的函数或者方法中,而rethrows针对的不是函数或者方法的本身,而是它携带的闭包类型的参数,当它的闭包类型的参数throws的时候,我们要使用rethrows继续将这个异常往上传递, 直到被调用者使用到。这相比throws多一个传递的环节。
    还是同样,使用一个简单的例子来看一看:

        func willThrow(_ type:Int)throws -> String{
            
            print("开始处理错误")
            if type == 1 {
                print("成功")
            }else if type == 2 {
                throw MyError.ErrorTwo
            }else if type == 3{
                throw MyError.ErrorThree
            }else {
                throw MyError.ErrorOther
            }
            return "一切都很好"
        }
        
    
        func willRethrow(_ throwCall:(Int) throws ->String) rethrows {
          
            do {
               let result = try throwCall(2)  
                print(result)
            } catch let err as MyError{
                throw err    //这里进行了 再次throw
            }catch{
                
            }
        }
    
    //调用
           let afunc = self.willThrow
            
            do {
                try self.willRethrow(afunc)
            } catch let err as MyError {
                print("rethorws ",err)
            }catch{
                
            }
    
     打印 //
      rethorws  ErrorTwo
    

    代码中看到,willRethrow本身并不对错误进行处理,原因是它本身并不会差生错误。另外的,它的参数throwCall进行了错误的处理,willRethrow对throwCall的错误进行再次throw。
    简单来说,rethorws就是throws的传递,也即是对于throw的一个层次级别的应用。我们甚至可以进行多级传递,但是会导致代码过于复杂,不建议这么做。

    相关文章

      网友评论

        本文标题:Swift中一些常见的关键字一(inout,defer,thro

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