来源

 

 

协议的语法(Protocol Syntax)

协议的定义:

protocol SomeProtocol {
    // 协议内容
}

实现多个协议,各协议之间用逗号分隔:

struct SomeStructure: FirstProtocol, AnotherProtocol {
    // 结构体内容
}

如果一个类在含有父类的同时也采用了协议,应当把父类放在所有的协议之前,如下所示:

class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
    // 类的内容
}

 

对属性的规定(Property Requirements)

protocol SomeProtocol {
    var mustBeSettable : Int { get set } // 可读可写的
    var doesNotNeedToBeSettable: Int { get } // 只读
}

在协议中定义类成员,使用class作为前缀,在枚举和结构体中实现协议时,需要使用static关键字作为前缀。

protocol AnotherProtocol {
    class var someTypeProperty: Int { get set }
}

对方法的规定(Method Requirements)

定义类方法和定义属性一样使用class属性,枚举、结构体实现时需要用static属性

protocol SomeProtocol {
    class func someTypeMethod() // 类方法
    func random() -> Double // 实例方法
}

 

该协议规定要求实现者要有一个someTypeMethod的类方法,且没有参数没有返回值。还有一个random()的方法,返回一个Double类型的值。

 

对变异方法的规定(Mutating Method Requirements)

实现协议中的mutating方法时,不用写mutating关键字;用结构体枚举实现协议中的mutating方法时,必须写mutating关键字。

protocol Togglable {
    mutating func toggle()
}
enum OnOffSwitch: Togglable {
    case Off, On
    mutating func toggle() { // 枚举实现的变异方法
        switch self {
        case Off:
            self = On
        case On:
            self = Off
        }
    }
}

 

对构造器的规定(Initializer Requirements)

protocol SomeProtocol {
    init(someParameter: Int)
}

该协议要求实现者必须实现一个int参数的构造器

class SomeClass: SomeProtocol {
    required init(someParameter: Int) { // 必须加上 required 修饰符
        //构造器实现
    }
}

必须给构造器实现标上"required"修饰符,使用required修饰符可以保证:所有的遵循该协议的子类,同样能为构造器规定提供一个显式的实现或继承实现。如果,该类被final修饰了,那就不用再加required修饰符,因为被final修饰的类不能有子类

 

如果一个子类重写了父类的指定构造器,并且该构造器遵循了某个协议的规定,那么该构造器的实现需要被同时标示requiredoverride修饰符

protocol SomeProtocol {
    init()
}

class SomeSuperClass {
    init() {
        //协议定义
    }
}

class SomeSubClass: SomeSuperClass, SomeProtocol {
    // "required" 来自于集成的协议; "override" 来自于父类
    required override init() {
        // 构造器实现
    }
}

 

协议类型(Protocols as Types)

 协议可以被当做类型来使用

使用场景:

  • 协议类型作为函数、方法或构造器中的参数类型或返回值类型
  • 协议类型作为常量、变量或属性的类型
  • 协议类型作为数组、字典或其他容器中的元素类型

 

在扩展中添加协议成员(Adding Protocol Conformance with an Extension)

扩展可以为已存在的类型添加属性方法下标脚本协议等成员。

TextRepresentable协议含有一个asText,如下所示:

protocol TextRepresentable {
    func asText() -> String
}

通过扩展为上一节中提到的Dice类遵循TextRepresentable协议

extension Dice: TextRepresentable {
    func asText() -> String {
        return "A \(sides)-sided dice"
    }
}

从现在起,Dice类型的实例可被当作TextRepresentable类型:

let d12 = Dice(sides: 12,generator: LinearCongruentialGenerator())
println(d12.asText())
// 输出 "A 12-sided dice"

SnakesAndLadders类也可以通过扩展的方式来遵循协议:

extension SnakesAndLadders: TextRepresentable {
    func asText() -> String {
        return "A game of Snakes and Ladders with \(finalSquare) squares"
    }
}
println(game.asText())
// 输出 "A game of Snakes and Ladders with 25 squares"

 

通过扩展补充协议声明(Declaring Protocol Adoption with an Extension)

当一个类型已经实现了协议中的所有要求,却没有声明时,可以通过扩展来补充协议声明:

struct Hamster {
    var name: String
    func asText() -> String {
        return "A hamster named \(name)"
    }
}
extension Hamster: TextRepresentable {}

Hamster的实例就可以作为TextRepresentable类型使用

let simonTheHamster = Hamster(name: "Simon")
let somethingTextRepresentable: TextRepresentable = simonTheHamster
println(somethingTextRepresentable.asText())
// 输出 "A hamster named Simon"

 

协议的继承(Protocol Inheritance)

协议能够多继承。语法与类的继承相似,多个协议间用逗号分隔:

protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
    // 协议定义
}

 

类专属协议(Class-Only Protocol)

你可以在协议的继承列表中,通过添加“class”关键字,限制协议由类来实现。

protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
    // class-only protocol definition goes here
}

在以上例子中,协议SomeClassOnlyProtocol只能被类(class)类型适配。如果尝试让结构体或枚举类型适配该协议,则会出现编译错误。

 

注意: 当协议需求定义的行为,要求(或假设)它的遵循类型必须是引用类型而非值类型时,应该采用类专属协议。

 

协议合成(Protocol Composition)

一个协议可由多个协议采用protocol<SomeProtocol, AnotherProtocol>这样的格式进行组合,称为协议合成

protocol Named {
    var name: String { get }
}
protocol Aged {
    var age: Int { get }
}
struct Person: Named, Aged {
    var name: String
    var age: Int
}
func wishHappyBirthday(celebrator: protocol<Named, Aged>) {
    println("Happy birthday \(celebrator.name) - you're \(celebrator.age)!")
}
let birthdayPerson = Person(name: "Malcolm", age: 21)
wishHappyBirthday(birthdayPerson)
// 输出 "Happy birthday Malcolm - you're 21!

wishHappyBirthday方法表示接收一个实现了Named和Aged的类型的实例

注意: 协议合成并不会生成一个新协议类型,而是将多个协议合成为一个临时的协议,超出范围后立即失效。

 

检验协议的一致性(Checking for Protocol Conformance)

使用isas操作符来检查协议的一致性或转化协议类型。

  • is操作符用来检查实例是否遵循了某个协议
  • as?返回一个可选值,当实例遵循协议时,返回该协议类型;否则返回nil
  • as用以强制向下转型。转型失败时会抛出异常
@objc protocol HasArea {
    var area: Double { get }
}

注意: @objc用来表示协议是可选的,也可以用来表示暴露给Objective-C的代码,此外,@objc型协议只对有效,因此只能在中检查协议的一致性。

如下所示,定义了CircleCountry类,它们都遵循了HasArea协议

class Circle: HasArea {
    let pi = 3.1415927
    var radius: Double
    var area: Double { return pi * radius * radius } // 只读的计算型属性
    init(radius: Double) { self.radius = radius }
}
class Country: HasArea {
    var area: Double // 存储型属性
    init(area: Double) { self.area = area }
}

// 没有实现HasArea协议的Animal类
class Animal {
    var legs: Int
    init(legs: Int) { self.legs = legs }
}

let objects: [AnyObject] = [
    Circle(radius: 2.0),
    Country(area: 243_610),
    Animal(legs: 4)
]

for object in objects {
    if let objectWithArea = object as? HasArea {
        println("Area is \(objectWithArea.area)")
    } else {
        println("Something that doesn't have an area")
    }
}
// Area is 12.5663708
// Area is 243610.0
// Something that doesn't have an area

 

对可选协议的规定(Optional Protocol Requirements)

可选协议含有可选成员,其遵循者可以选择是否实现这些成员。在协议中使用@optional关键字作为前缀来定义可选成员。可选协议在调用时使用可选链

someOptionalMethod?(someArgument)这样,你可以在可选方法名称后加上?来检查该方法是否被实现。可选方法可选属性都会返回一个可选值(optional value),当其不可访问时,?之后语句不会执行,并整体返回nil

注意: 可选协议只能在含有@objc前缀的协议中生效。且@objc的协议只能被遵循

@objc protocol CounterDataSource {
    optional func incrementForCount(count: Int) -> Int // 可以选择是否实现
    optional var fixedIncrement: Int { get }  // 可以选择是否实现
}

 

2015-03-25

20:14:22

posted on 2015-03-25 20:15  道无涯  阅读(143)  评论(0编辑  收藏  举报