iOS编程基础-Swift(五)-流程控制与其他

Swift(iOS9 Programming Fundamentals With swift


第五章 流程控制与其他

         

         本章介绍Swift语言剩余的其他方面;

         1)Swift分支、循环与跳转流程控制结构的语法;

         2)如何重写运算符以及如何创建自定义运算符;

         3)Swift的隐私特性与内省特性;

         4)以及用于引用类型内存管理的专用模式;

         

         5.1 流程控制

         

         计算机程序都有通过代码语句表示的执行路径:连续执行每一条语句;

         流程控制用于让执行路径调到某些代码语句,或是重复执行一些代码语句;

         

         通过测试条件的真值,程序可以确定如何继续;

         基于条件测试的流程控制大体上可以分为以下两种类型;

         1)分支

             代码被划分为不同的区块,像分叉路一样,程序有几种可能进行下去的方式:条件真值用于确定哪一个代码区块会被真正执行;

         2)循环

             将代码块划分出来以重复执行:条件真值用于确定代码块是否应该执行,然后是否应该再次执行;每次重复都叫做一次迭代;一般来说,每次迭代时都会改变一些环境特性(比如,变量的值),这样重复就不是一样的了,而是整个任务处理中的连续阶段;

         

         控制流程中的代码块(称为块)是由花括号包围的,这些花括号构成了一个作用域;

         可以在里面声明新的局部变量,离开花括号之后,会自动消亡;

         

         与C不同,在Swift中,条件不必放到圆括号中,不过花括号是不能省略的;此外,Swift还添加了一些专门的流程控制特性,帮助更好的使用Optional;

         

         5.1.1 分支

         

         Swift有两种形式的分支:if结构以及switch语句;此外本节还会介绍 条件求值,它是if结构的一种紧凑形式;

         

         1. if结构

         

         if condition{

             statements

         }

         if...{ }else{ }

         if...{ }else if...{ }else{ }

         

         自定义嵌套作用域:

             有时,当知道某个局部变量只需要在几行代码中存在时,你可能会自己定义一个作用域——自定义嵌套作用域;在作用域开头引入局部变量,在作用域结尾会自动销毁;

         不过Swift不允许你使用空的花括号来这么做;而是用如下这种结构:

         

        do {
                
        }

        

         2. 条件绑定

         if后紧跟变量的声明和赋值,叫做条件绑定,形式如 if let/var name:Type = value ;

         它实际上是条件展开Optional的缩写,赋的value值是个Optional,如果不是,编译器会报错:

         1)如果Optional为nil,那么条件会失败,块也不会执行;

         2)如果Optional不为nil,那么:

         ·Optional会被展开;

         ·展开的值会被赋值给声明的局部变量;

         ·块执行时局部变量会处于作用域中;

         因此,条件绑定是将展开的Optional安全地传递给块的一种便捷方法,只有Optional可展开块才会执行;

         

         条件绑定中的局部变量可以与外部作用域中的已知变量同名,他甚至可以与被展开的Optional同名;这样就不会无意中访问Optional;(我一般都会用一个简写)

         

         如:(仅作为示例说明,类似的场景比较多)

         if let prog = (notify.userinfo?["progress"] as? NSNumber)?.doubleValue{

             self.progress = prog

         }

         

         当然,也可以进行条件绑定的嵌套:

         if let u = notify.userinf{

             if let prog:AnyObject = u["progress"]{

                 if let prog = prog as? NSNumber {

         

                 }

             }

         }

         

         为了避免缩进,可以将连续的条件绑定放到一个列表中,中间用逗号隔开:

         if let u = notify.userinf, prog:AnyObject = u["progress"] as? NSNumber {

             //注意=号后边是一个整体

         }

         

         列表中的绑定甚至可以后跟一个where子句,将另一个条件放到一行当中;这个子句对已经绑定的局部变量做了进一步的限制;

         

         现在,你也可以通过一些列的guard语句来表示这个可选链:

         guard语句:表示满足条件则继续;

             guard...else{ }

         对应可选绑定的话,则为 guard if let/var name:Type = value else{ },value可解包,name有值,则继续,否则执行else中内容,一般是return;

         

         3. Switch语句

         

         Switch是一种更为简洁的if...else if...else结构编写方式(C中Switch有个隐藏的陷阱,Swift消除了这个陷阱,并增加了功能和灵活性);

         

         在switch中,条件位于不同可能值与单个值的比较(叫做标记)中,叫做case;

         case比较是按照顺序执行的,如果单个case比较成功,那么case的代码就会执行,整个switch语句将会退出;

         

         switch tag {//tag就是标记

         case pattern1:

             statements

         case pattern1:

             statements

         default:

             statements

         }

         

         在Swift中,Switch语句必须是完备的,这意味着标记的每个可能值都必须要被case覆盖到,常见的扫尾case是使用一个default 的case;

         

         每个case至少要包含一行代码,最少的单行case代码只包含关键字break;这种情况下,break表示一个占位符,什么都不做;

         很多时候,switch语句会包含一个default(或其他扫尾case),他只包含了关键字break;这样就可以穷尽标记的所有可能值;

         

         Swift中,case值实际上是一个叫做模式的特殊表达式,该模式会通过“隐私的模式匹配运算符~=”与标记值进行比较;

         

         诸多使用场景:

         ·模式还可以包含一个下划线(_)来表示所有其他值;它是扫尾case的一种替换形式;

         

       var i = 4
        switch i {
        case 1:
            print("You have 1 thingy!")
        case _:
            print("You have many thingys!")//You have many thingys!
        }

        

        ·模式可以包含局部变量的声明(无条件绑定)来表示所有值,并将实际值作为该局部变量的值;它是扫尾case的另一种替换方案:

         

        switch i {
        case 1:
            print("")
        case let n:
            print("\(n)")//4
        }  

        

         ·如果标记是个Comparable,那么case还可以包含Range;比较时会向Range发送contains消息;

         

        switch i {
        case 1:
            print("")
        case 2...10:
            print("\(i)")//4
        default:
            break
        }

        

         ·如果标记是一个Optional,那么case可以将其与nil进行比较;

         这样,安全展开Optional的一种方式就是先将其与nil进行比较,并在随后的case中将其展开;

         

        let j:Int? = 5
        switch j {
        case nil:
            break
        default:
            switch j! {
            case 1:
                print("")
            case let n:
                print("\(n)")//5
            }
        }
        //也可以将?追加到case模式上,安全地展开一个Optional;
        switch j {
        case 1?:
            print("1")
        case let n?:
            print("\(n)")//5
        case nil:
            break
        }

         

         ·如果标记是一个bool值,那么case就可以将其条件进行比较了;

         将true作为标记,就可以通过case测试任何条件;这样switch就变成了扩展的if...else if结构的替代者;

         

        let x = 0
        let y = 1
        switch true {
        case x < y:
            print("x < y")//x < y
        case x == y:
            print("x == y")
        default:
            print("x > y")
        }

         

         ·模式还可以包含where子句来添加条件,从而限制case的真值;他常常会与绑定搭配使用,但这并非强制要求;条件可以引用绑定中声明的变量;

         case let j where j > 0:

         ·模式可以通过is运算符来判断标记的类型;

         case is NoisyDog:

         ·模式可以通过as(不是as?)运算符进行类型转换;

         case let d as NoisyDog:  //表示可以被转换为NoisyDog的所有case真值;

         ·在与特定的值进行比较时,你还可以使用as(不是as!)运算符根据情况对标记(注意是对标记)进行向下类型转换;

         case 0 as Int: //对应可以将tag转换为Int类型之后,值是0的情况;

         ·你可以将标记表示为元组,同时将相应的比较也包装到元组中,这样就可以同时进行多个比较了;

         只有当比较元组与相应的标记元组中的每一项比较都通过,这个case 才算通过;

         case let (size as Int , desc as String):

         ·如果标记是个枚举,那么case就可以是枚举的case;

         如果case覆盖了所有的情况,那么就可以不需要扫尾case了;

         

         关于枚举case中抽取关联值:

         1)Switch语句提供了从枚举case中抽取出关联值的方式;

         

       enum ErrorA {
            case Number(Int)
            case Message(String)
            case Fatal
        }
        
        let error = ErrorA.Number(7)
        switch error {
        case .Number(let n):
            print("\(n)")//7
        case let .Message(s) where s.characters.count > 0:
            print(s)
        default:
            print("Fatal")
            break
        }

        

         回忆一下:关联值实际上是一个元组;匹配case名后的模式元组会应用到关联值上;如果模式是个绑定变量,那么他会捕捉到关联的值;

         let/var可以位于圆括号中,或是在case 关键字后;如上面代码演示的;

         如果let(或var)位于case关键字之后,那么就可以添加一个where子句;

         

         2)如果不想抽取错误号,只想进行匹配,那就可以在圆括号中使用另外一种模式;

         

        switch error {
        case .Number(1..<Int.max):
            print("正数")//正数
        default:
            print("其他")
        }
        

         我们可以通过轻量级的if case结构在条件中使用与switch语句的case相同模式的语法,来进行关联值的抽取;if case后面会跟着一个等号和标记;

         

        if case let .Number(n) = error {
            print(n)//7
        }     

         

         甚至还可以在switch case中附加一个where子句,和之前介绍的一样;

         

         组合case:

         1)要想将case测试组合起来(使用隐式的逻辑或),可以用逗号将其分隔开;

         

        i = 5
        switch i {
        case 1,3,5,7:
            print(i)//5
        default:
            print("no pattern")
        }
        
        switch i {
        case is Int , is Double:
            print("number")//number
        default:
            print("other")
        }

        

         不过你不能使用逗号组合声明绑定变量的模式,因为这种情况下,对变量的赋值是不确定的;

         

         2)组合case的另一种方式是通过fallthrough语句从一个case落到下一个case上;

         注意,fallthrough会跳过下一个case的测试;他会直接开始执行下一个case的代码;因此,下一个case不能声明任何绑定变量,因为无法对变量赋值;

         

         4. 条件求值

         

         使用分支语句作为变量值会更好些,比如将变量赋值语句等号后直接跟着一个分支结构——有些语言允许这样,但是Swift不行,不过,我们可以使用定义与匿名函数调用:


        enum SortA :String{//注意这种类型的枚举 是不能进行关联值提取的,它的关联值实际上是一个空元组“()”;他持有的只是一个原生值
            case Al
            case Bl
        }
        let sortA = SortA.Al
        print(sortA.rawValue)//Al
        
        enum SortB {
            case Al(Int)
            case Bl(String)
        }
        let sortB = SortB.Al(6)
        
        let title:String = {
            switch sortB {
            case let .Al(s):
                return "\(s)"//6
            case let .Bl(s):
                return s
            }
        }()
        print(title)
        

         这种在进行分支判断然后赋值的场景应该会很有用;

         

         三元运算符:

         有时,值通过一个二路条件才能确定下来,Swift提供了一个类似C语言的三元运算符,形如 condition ? exp1 : exp2

         

         nil合并运算符:

         如果被测试的Optional为nil,那就会使用替代值;否则会展开Optional,并使用被包装的值,即??运算符;叫做nil合并运算符;

         

         回忆第四章的示例:

 

        let arr :[Int?] = [1,nil,2,nil,4]
        let arr2:[AnyObject] = arr.map{if $0 == nil {return NSNull()} else { return $0! as AnyObject }}
        print(arr2)//[1, <null>, 2, <null>, 4]
        
        //使用三元运算符
        let arr3:[AnyObject] = arr.map{$0 == nil ? NSNull() : $0! as AnyObject}
        print(arr3)//[1, <null>, 2, <null>, 4]
        
        //使用nil合并运算符
        let arr4 = arr.map{ $0 ?? -1}
        print(arr4)//[1, -1, 2, -1, 4]
      

         还可以将使用??的表达式链接起来:

         let somenumber = i1 as? Int ?? i2 as? Int ?? 0

         

         上述代码尝试将i1类型转换为Int并使用该Int;如果失败,那么他会尝试将i2类型转换为Int并使用该Int;如果这也失败就会放弃并使用0.

         

         5.1.2 循环         

         循环的目的在于重复一个代码块的执行,并且在每次迭代时会有一些差别;这种差别通常还作为循环停止的信号;

         Swift提供两种循环结构:while循环与for循环;

         1. while循环

         while循环有两种形式:

         while condition {

             statements

         }

         

         repeat{

             statements

         } while conditon

         

         第二种代码块至少运行一次;

         一般来说,块中的代码会修改一些东西,这回影响环境与条件,最终让循环结束;

         

         使用:

         1)while let

         while循环的第一种形式的条件可以是Optional的条件绑定,知道Optional为nil;

         如:while let p = self.movenda.popLast(){}

         

         2)while循环的一种常见用法是沿着层次结构向上或向下遍历;条件可以设置在where子句中;

         如:

         var v :UIView = textField

         while let vv = v.superview where !(vv is UITableViewCell){v = vv}

         if let c = v.superview as? UITableViewCell{...}

         

         这段代码是安全的,while let 进行可选绑定,退出条件既可以是vv = nil(即没找到),也可以是where子句不满足(找到了);之后再做可选的类型装换,如果找到了,那么c就是我们最终找到的父视图;

         

         3)while case

         类似if case结构,在while case 中也可以使用switch case模式;

         

        enum ErrorH {
            case Message(String)
            case Number(Int)
            case Fatal
        }
        let arrH:[ErrorH] = [.Message("hahaha"),.Message("flower"),.Number(5),.Message("flower1"),.Fatal]
        var ids:Int = 0
        while case let .Message(str) = arrH[ids] {
            print(str)//hahaha  flower
            ids = ids + 1
        }

        

         该循环会从开始位置提取出与.Message关联的字符串值,遇到无法提取的则循环终止;

         

         2. for循环

         for循环的两种形式:

         for variable in sequence {

             statements

         }

         

         for before-all ; condition ; after-each {

             statements

         }

         

         OC中的for-in要求,一个类遵循了NSFastEnumeration协议;

         Swift中,要求一个类型使用了SequenceType协议;

         

         在for-in结构中,变量会在每次迭代中隐式通过let进行声明;因此在默认情况下它是不可变的(如果需要对变量进行赋值,那么请写成for var);该变量对于块来说也是局部变量;

         

         SequenceType又一个makeIterator方法,他会生成一个‘迭代器’对象,这个迭代器对象本身又一个mutating的next方法,该方法会返回序列中的下一个对象,并被包装到Optional中,如果没有下一个对象,那么他会返回nil;

         for-in循环过程就在不断调用迭代器的next方法,直到返回nil:

         

        var g = (1...5).makeIterator()
        while let i = g.next() {
            print(i)
        }   

        

         使用:

         1)遇到来自OC的数组,需要从AnyObject进行向下类型装换

         如: for boy in p.boys() as! [String] {}

         

         2)序列的enumerate方法会生成一个元组序列,并在原始序列的每个元素前加上其索引号:

         

        let titlesA = ["a","b","c","d"]
        for (ids,value) in titlesA.enumerated() {
            print("\(ids)" + value)
        }

        

         输出如下:

        0a

        1b

        2c

        3d

 

        

         3)附加where子句:进一步进行值的筛选;

         4)for case 模式匹配:

 

       for case let .Number(n) in arrH {
            print(n)//
        }

        

         5)可以通过调用stride方法来生成序列,它是strideable协议的一个实例方法;并且可被数字类型与可增加及减少的所有类型所使用,他拥有两种形式:

         stride(from: , through: , by: )    包括最终值

         stride(from: , to: , by: )         不包括最终值,by指的是间隔

         

        for item in stride(from: 1, to: 5, by: 2){
            print(item)//1 3
        }
        for item in stride(from: 1, through: 5, by: 2){
            print(item)//1 3 5
        }

        

         6)zip函数:同时遍历两个序列,生成一个Zip2结构体,本身也是一个序列;值是原来两个序列中相应元素所构成的元组;较长序列的元素会被忽略;


        let arrX = [1,3,5,7]
        let arrY = ["a","b","c","d","e"]
        for (item1 , item2) in zip(arrX, arrY) {
            print("\(item1)" + item2)//1a 3b 5c 7d
        }  

      

        第二种形式的for循环:

         来源于C的循环,主要用于增加或减少计数器值;

         before-all语句会在进入for循环时执行一次,通常用于计数器的初始化;

         接下来是测试条件,为true则代码会执行;条件通常用来测试计数器是否达到了某个限值;

         接下来就是执行after-each语句,通常用于增加或减少计数器值;接下来会再次测试条件;

        


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值