这个教程要求Xcode7和Swift2,在这里还是测试版,大家可以去下载最新的.



在wwdc2015,发布了Swift2,包含了新的特性来提高你写代码的方式.



最令人兴奋的特性是协议拓展(protocol extensions) . 在swift1是可以拓展已存在功能性的class,struct,enum类型. 在swift2,你还可以拓展protocol.



尽管最初看起来像一个次要的功能, protocol extensions 确实非常有用,能够改变你写代码的方式. 在这个教程,你会探索生成和使用protocol extensions的几种方式以及新的技巧和面向协议编程的设计模式,它们会让你的代码更好.



你也能看到Swift team怎样使用protocol extensions 来提高Swift的标准库的,它是如何影响你写代码的方式.



开始啦



先创建playground,名字为swift-Procotols. 添加以下代码:


{        
           var name        :         String         {         get         }        
           var canFly        :         Bool         {         get         }        
}        
          
 protocol Flyable         {        
           var airspeedVelocity        :         Double         {         get         }        
}



这里定义了一个简单的protocol Bird 和简单的properties name和canFly 和 Flyable协议,它定义 airspeedVelocity变量.



在以前,你可能构建一个含有Flyable的基类,然后依赖于继承来定义Bird或者其他能fly的东西,例如airplanes. 在这里请记住:任何东西都从一个协议开始!


当你下一步开始定义实例的时候,你会发现它是怎样让整个系统更加灵活的.



定义 Protocol-conforming 类型



添加一下结构体定义到刚刚的代码下面:


struct FlappyBird        : Bird, Flyable         {        
           let name        :         String        
           let flappyAmplitude        :         Double        
           let flappyFrequency        :         Double        
           let canFly         =         true        
          
           var airspeedVelocity        :         Double         {        
             return         3         * flappyFrequency         * flappyAmplitude        
           }        
}



这定义了一个新的结构体FlappyBird, 它遵循Bird和Flyable protocols. 它的airspeedVelocity 是通过flappyFrequency和flappyAmplitude组成的一个函数来计算的.要flappy,就返回sure给canFly.



下一步,在添加两个结构体定义到刚才的代码下面


struct Penguin        : Bird         {        
           let name        :         String        
           let canFly         =         false        
}        
          
struct SwiftBird        : Bird, Flyable         {        
           var name        :         String         {         return         "Swift \(version)"         }        
           let version        :         Double        
           let canFly         =         true        
          
           // Swift is FAST!
           var airspeedVelocity        :         Double         {         return         2000.0         }        
}


z


一个Penguin 是一个Bird,但是不能fly.这是一个你没有利用继承方式的一个好处. 毕竟不是所有的birds都能fly.  一个SwiftBird当然是很快的,有着一个很高的airspeed velocity.


你已经发现了还有一些冗余. 每种类型的Bird都必须声明canFly属性,尽管已经有一个Flyable概念在你的系统.



用默认实现来扩展协议extenting Protocols



有了protocol extensions, 你能定义一个协议的默认行为. 添加下面代码到Bird协议定义:


: Flyable         {        
           // Flyable birds can fly!
           var canFly        :         Bool         {         return         true         }        
}



这定义了一个Bird protocol的扩展,它设置了canFly为true的默认行为,不管类型是不是Flyable. 换言之,任何Flyable的bird不再需要显性声明了!




               我不能总是定义我自己的协议,但当我定义了自己的协议,我会用默认行为来扩展它们.




Swift1.2 介绍了协议扩展是需通过if-let语法来实现的,而Swift2带来了更好的条件扩展协议.


在FlappyBird和SwiftBird结构体定义中,删除let canFly = true. 你会发现playground成功编译,因为protocol extension现在会处理你的要求了.




为什么不使用基类呢?




Protocol extensions 和默认实现可能看起来和使用基类或者说是其他语言中的抽象类, 但protocol extensions提供了一些在Swift关键的优势:


  •     因为types 可以遵循多个协议,它们可以设置多个协议的默认行为. 不像一些语言的类能够实现多继承,协议扩展不引入任何额外的状态. 
  • Protocol 能够被classes, struct和enums遵循. 基类和继承被只能是class types.



换言之,Protocol extensions 让我们可以为value types 定义默认行为,而不仅仅是class.



你已经看到这种做法应用于结构体, 下面, 添加下面的enum 定义到playground的最后.


enum UnladenSwallow        : Bird, Flyable         {        
           case African        
           case European        
           case Unknown        
          
           var name        :         String         {        
             switch         self         {        
               case .African        :        
                 return         "African"        
               case .European        :        
                 return         "European"        
               case .Unknown        :        
                 return         "What do you mean? African or European?"        
             }        
           }        
          
           var airspeedVelocity        :         Double         {        
             switch         self         {        
               case .African        :        
                 return         10.0        
               case .European        :        
                 return         9.9        
               case .Unknown        :        
         fatalError        (        "You are thrown from the bridge of death!"        )        
             }        
           }        }




就是其他value type一样, 你需要做的就是定义正确的properties,  例如UnladenSwallow 遵循2个协议. 因为它遵循了Bird和Flyable协议, 所以它设置canFly默认实现.



扩展Protocols



通常最通用的协议扩展时候是扩展额外的协议,无论是在Swift标准库还是第三方的framework.



添加下面代码到playground:


{        
           func skip        (skip        :         Int        )         ->         [Generator.Element        ]         {        
     guard skip         !=         0         else         {         return         [        ]         }        
          
             var index         =         self.startIndex        
             var result        :         [Generator.Element        ]         =         [        ]        
             var i         =         0        
     repeat         {        
               if i         % skip         ==         0         {        
         result.append        (        self        [index        ]        )        
               }        
       index         = index.successor        (        )        
       i        ++        
             }         while         (index         !=         self.endIndex        )        
          
             return result        
           }        }


这定义了CollectionType的扩展,定义了一个新的skip(_:)方法,这个方法跳过collection里面每个skip元素,返回那些未被跳过的元素组成的一个数组.



CollectionType 是一个协议,被Swift中的arrays和dictionaries的type遵循. 这意味着新的行为存在于每个CollectionType在你的App里! 添加下面的代码到playground最下面:


let bunchaBirds        :         [Bird        ]         =        
           [UnladenSwallow.African,        
    UnladenSwallow.European,        
    UnladenSwallow.Unknown,        
    Penguin        (name        :         "King Penguin"        ),        
    SwiftBird        (version        :         2.0        ),        
    FlappyBird        (name        :         "Felipe", flappyAmplitude        :         3.0, flappyFrequency        :         20.0        )        ]        
          (         3         )



这里,你是定义一个birds的array,包含很多你已经定义了的类型. 因为arrays遵循了CollectionType.那意味着,它们有skip(_:)方法是可用.



扩展你们自己的协议



就像添加一个新的行为到来自Swift标准库的protocols里面,你也可以定义默认的行为.


修改Bird Protocol,声明遵循BooleanType protocol:


: BooleanType          {


遵循BooleanType 意味你的type需要有BoolValue property 是它能像一个Boolean. 是否意味着你必须添加在这个property 到每个Bird type?



当然不是,有一个更简单的方式,就是协议拓展. 添加代码到Bird定义下面:


: Bird         {        
           var boolValue        :         Bool         {        
             return         self.canFly        
           }        }


这拓展使canFly property 代表 每个Bird type的Boolean value.



尝试,添加下面代码到playground:


if UnladenSwallow.African         {        
           print        (        "I can fly!"        )        
}         else         {        
           print        (        "Guess I’ll just sit here :["        )        }



你应该会看到”I can fly!” 被打印出来. 但尤其,你需要使用African Unladen Swallow 在一个条件语句.



对Swift标准库的影响



你已经看到protocol extensions 是一个很好的方式来自定义和扩展你代码的能做的事情,以及协议定义在你的aoo之外. 你可能会惊讶,Swift 协议扩展,也用于提升标准库的写法.



Swift通过添加map,reduce,filter方法来提高函数编程的范例. 这些存在于不同的CollectionType成员,例如Array:


// 计算数组中字符个数


[         "frog",          "pants"         ].         map          { $         0.length          }.         reduce         (         0         )          { $         0          + $         1          }          // 返回9



对一个数组调用map,返回另外一个数组.  而reduce被调用减少结果到最后value 9.


在这个例子中,map和reduce是包含在Array中,是Swift标准库的一部分. 如果你cmd-clicked在map上,你会看到它是怎么定义的.


在Swift1.2 你会看到下面:


// Swift 1.2
 extension         Array         : _ArrayType         {        
           /// Return an `Array` containing the results of calling
           /// `transform(x)` on each element `x` of `self`
           func map<U>        (transform        :         (T        )         -> U        )         ->         [U        ]        }


map函数在这里是定义的Array的一个拓展. Swift的函数式的函数使用不仅在Array上. 然而,而是在所有的CollectionType 上. 那Swift1.2是怎样实现它的?



如果你在一个Range上调用map,以及Cmd-clicked 在map上,你会看到下面的定义:


// Swift 1.2
 extension Range         {        
           /// Return an array containing the results of calling
           /// `transform(x)` on each element `x` of `self`.
           func map<U>        (transform        :         (T        )         -> U        )         ->         [U        ]        
}



结果表明在Swift1.2,在Swift标准库里,map需要被每个Collectiontype重定义. 这是因为尽管Array和Range都是CollectionType 但是structs不能被子类化,所以不能有相同的实现.



这不是Swift标准库如何被构建的细微差别,这是约束了你对Swift types 能做的事情.



下面的函数,传入Flyable 的CollectionType 返回含有最高的airspeedVelocity的元素.


func topSpeed<T        : CollectionType where T.GeneratorType        : Flyable>        (collection        : T        )         ->         Double         {        
   collection.        map         { $        0.airspeedVelocity         }.        reduce         {         max        ($        0, $        1        )        }        }



在Swift1.2 没有protocol extensions,这实际上是一个构建失误. map和reduce函数只存在于一组预定义types,并不适用于所有CollectionType.



有Swift2.0 和 Protocol extensions , map的定义在Array和Range如下:


/ Swift 2.0
 extension CollectionType         {        
           /// Return an `Array` containing the results of mapping `transform`
           /// over `self`.
           ///
           /// - Complexity: O(N).
           func map<T>        (@noescape transform        :         (Self.Generator.Element        )         -> T        )         ->         [T        ]        }


尽管你不能看到map的源码. swift2会开源. CollectionType 的map现在有一个默认的实现,所有CollectionType都有效.



添加如下代码:


func topSpeed<T        : CollectionType where T.Generator.Element         == Flyable>        (c        : T        )         ->         Double         {        
           return c.        map         { $        0.airspeedVelocity         }.        reduce        (        0        )         {         max        ($        0, $        1        )         }        }



map和reduce方法,Flyable实例的collection都能使用了. 现在,添加下面代码,你就能回答,它们之中谁最快的问题了.


let         flyingBirds        :                 [        Flyable        ]                 =         
           [        UnladenSwallow.African,
   UnladenSwallow.European,
   SwiftBird        (        version        :                 2.0        )        ]        
          topSpeed         (         flyingBirds         )                   // 2000.0




如何进行下一步学习? 



你已经看到了面向协议开发, 建立自己的简单的协议,使用协议扩展来扩展它们. 用默认的实现,你可以给已存在的协议添加共有的或自动的行为,很像一个基类,但比基类更好,因为能应用到struct和enums.



另外,协议扩展不仅可以使用到扩展你自己的协议,也可以扩展和提供默认行为到Swift标准库,Cocoa和Cocoa Touch的协议里.


 


     你应该看看Swift2.0令人兴奋的新特性.