Swift-11-协议(Protocols)

时间:2024-07-20 09:38:08

  协议定义了一个蓝图,规定了用来实现某一特定工作或者功能所必须的方法和属性。类、结构体或者枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能。任意能满足协议要求的类型被称为遵循conform这个协议。

  除了遵循协议的类型必须实现那些指定的规定以外,还可以对协议进行扩展,实现一些特殊的规定或者一些附加的功能,使得遵循的类型能够受益。

  协议的公式:

protocol SomeProtocol { // 协议内容
}

  要使类遵循某个协议,需要在类型名称后加上协议名称,中间以冒号:分隔,作为类型定义的一部分,遵循多个协议时,各协议之间用逗号,分隔。

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

  如果类在遵循协议的同时拥有父类,应该将父类名放在协议名之前,以逗号分隔。

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

  ****对属性的规定

  协议可以规定其 遵循者提供特定名称和类型的实例属性(instance property)或类属性(type property),而不用指定是存储型属性(stored property)还是计算型属性(calculate property),此外还必须指明是可读的还是可读可写的。

  如果协议规定属性是可读可写的,那么这个属性不能是常量或只读的计算属性。如果协议只要求属性是只读的(gettable),那么属性不仅可以是只读的,如果你代码需要的话,也可以是可写的。

  协议中的通常用var来声明变量属性,在类型声明后加上{set get}来表示属性是可读可写的,只读属性则用{get}来表示。

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

  在协议中定义类属性(type property)时,总是使用static 关键字作为前缀。当协议的遵循者是类时,可以使用class 或static关键字来声明类属性。

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

  例子来了····

protocol FullyNamed{
var fullName:String {get}
}

  FullyNamed协议除了要求协议的遵循者提供全名属性外,对协议对遵循者的类型并没有特别的要求。这个协议表示,任何遵循FullyNamed协议的类型,都具有一个可读的String类型实例属性fullyName。

//一个遵循FullNamed协议的简单结构体
struct Person:FullyNamed{
var fullName:String
}
let john = Person(fullName:"John Appleseed")

  这个例子定义了一个叫做Person的结构体,用来表示具有名字的人,从第一行代码中可以看出,它遵循了FullyNamed协议。

  Person结构体的每一个实例都有一个String类型的存储型属性fullName。这正好满足了FullyNamed协议的要求,也就意味着,Person结构体完整地遵循了协议。---如果协议要求未被完全满足,在编译时会报错

class Starship:FullyNamed{
var prefix:String?
var name:String
init(name:String, prefix:String? = nil){
self.name = name
self.prefix = prefix
}
var fullName:String{
return (prefix != nil ? prefix! + "":"")+name
}
}
var ncc = Starship(name:"Enterprise", prefix:"USS")

  将fullName属性实现为可读的计算型属性。

  ******对方法的规定

  协议可以要求其遵循者实现某些指定的实例方法或类方法。这些方法作为协议的一部分,像普通的方法一样放在协议的定义中,但是不需要大括号和方法体。可以在协议中定义具有可变参数的方法,和普通的方法定义方式相同。但是在协议的方法定义中,不支持参数默认值。

  正如对属性的规定中所说的,在协议中定义类方法的时候,总是使用static关键字作为前缀。当协议的遵循者是类的时候,你可以在类的实现中使用class或者static来定义类方法:

protocol SomeProtocol{
static func someTypeMethod()
}

  下面的例子定义了含有一个实例方法的协议:

protocol RandomNumberGenerator{
func random() -> Double
}

  ******对Mutating方法的规定

  有时需要在方法中改变它的示例。eg:值类型(结构体、枚举)的实例方法中,将mutating关键字作为函数的前缀,写在func之前,表示可以在该方法中修改它所属的实例及其实例属性的值。

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

  *******对构造器的规定

  协议可以要求它的遵循者实现特定的构造器,可以在协议的定义里写下构造器的声明,但是不需要写花括号和构造器的实体。

protocol SomeProtocol{
init(someParameter:Int)
}

  协议构造器规定在类中的实现

  可以在遵循该协议的类中实现构造器,并指定其为类的指定构造器(designated initializer)或者便利构造器(convenience initializer).在这种情况下,你都必须给构造器标上"required"修饰符。

  

class SomeClass:SomeProtocol{
required init(someParameter:Int){
//构造器的实现
}
}

  使用required修饰符可以保证:所有的遵循该协议的子类,同样能为构造器规定提供一个显式的实现或者继承实现。

  如果类已经被标记为final,那么不需要在协议构造器的实现中使用required修饰符。因为final类不能有子类。

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

  

protocol SomeProtocol{
init()
} class SomeSuperClass{
init(){
//构造器的实现
}
} class SomeSubClass:SomeSuperClass,SomeProtocol{
//因为遵循协议,需要加上required;因为继承自父类,需要加上override
required override init(){ }
}

  ****可失败构造器

  可以通过给协议Protocols中添加可失败构造器来使遵循该协议的类型必须实现该可失败构造器。

  如果在协议中定义一个可失败构造器,则在遵循该协议的类型中必须添加同名同参数的可失败构造器或非可失败构造器。如果在协议中定义一个非可失败构造器,则在遵循该协议的类型中必须添加同名同参数的非可失败构造器或隐式解析类型的可失败构造器(init!).

  *****协议类型

  尽管协议本身并不实现任何功能,但是协议可以被当做类型来使用。

  协议可以像其他普通类型一样使用,使用场景:
  1.作为函数、方法或者构造器中的参数类型或者返回值类型;

  2、作为常量、变量或者属性的类型;

  3.作为数组、字典或者其他容器中的元素类型。

  Attention:

  协议是一种类型,因此协议类型的名称应与其他类型(Int ,Double,String)的写法相同,使用大写字母开头的驼峰式写法。