协议、委托(代理)模式
注:本小节总结协议以及依靠协议实现委托,这将在以后经常被使用。是一个非常重要的模块
看下官方的定义:协议定义了一个蓝图 , 规定了用来实现某一特定工作或者功能所必须的方法和属性,类、结构体、或者枚举类型都可以遵循协议, 并提供具体实现来完成协议定义的方法和功能 。 任意能够满足协议要求的类型都被成为遵循了这个协议
1、协议的语法
协议的关键字:protocol
协议的语法:
protocol Pro1{
//这里定义属性或者方法
}
要使一个类或者结构体遵循某个协议 , Class1: Pro1
只需要冒号加上协议名称就可以了 。 这时候这个类就必须实现协议中的属性和方法 。(可选的可以不实现,后面会说)
2、 协议中对属性和方法的使用
- 对属性的使用
如果一个协议中定义的变量是可读可写的 那么遵循这个协议的必须是可读可写 ,也不能是常量 ,如果协议是只读的 , 实现者可以是只读 也可以是可读可写
在协议中使用类型属性 总是使用static
protocol Pro1{
var name:String{set get} //可读可写
var age:Int { get } //只读属性
static var status:Int {set get}
}
//遵循协议
struct Struct1 : Pro1 {
private var _name = ""
var name:String {
set{
_name = newValue
}
get{
return _name
}
}
var age = 18 //可以声明为可读可写的
static var status = 1 //类型方法。
}
class Class1: Pro1 {
var name = "class"
var age:Int{ return 19 } //也可以是只读
static var status = 0
}
这里可以看到遵循者实现协议的集中方式,包括可读可写的和只读的。注释非常清晰,就不再赘述。
在协议中你可以定义实例方法 , 也可以定义类方法 ,其方法和属性定义差不多。
protocol MethodPro{
func plus(a:Int,b:Int)->Int
static func toUpper(str:String)->String
}
class MethodClass: MethodPro {
func plus(a: Int,b: Int)->Int {
return a + b
}
class func toUpper(str: String)->String {
return str.uppercaseString
}
}
很简单的例子说明下 , 在协议中用static
在实现类里可以用static
或者 class
- 在协议中使用mutating 变异方法 。
我们都知道,在结构体和枚举这种值类型的实例方法中 , 不能对自己的变量进行操作 , 如果一定要操作 ,就需要生命成变异方法 。 所在在协议中有时候也需要声明成变异方法让结构体或者媒体去实现 。(类实现的时候不需要加mutating)
protocol MulPro{
mutating func toggle();
}
enum Tog:MulPro{
case On,Off
mutating func toggle() {
switch self {
case On:
self = Off
case Off:
self = On
}
}
}
var t=Tog.Off
t.toggle()
print(t) //Tog.On
t.toggle()
print(t) //Tog.Off
注释很清楚就不用过多解释了
- 协议中的构造器
协议也可以要求自己的遵循者实现指定的构造器。
所以在协议中也可以这么写,
protocol A{
init(val:Int)
}
实现这个协议的必须有这样的构造器,可以是指定构造器或者便利构造器,都必须加上required
修饰符 ,这样 在所以继承这个类的子类也都必须实现这个构造器。(关于各种构造器的解释 看我以前的构造过程那个文章)
class B: A { required init(val:Int){ } }
注:如果类已经标记为final ,则不需要required修饰符 ,final类不能有子类
如果在协议中定义的是一个可失败构造器,那么在遵循者中必须实现可失败构造器 后者 非可失败
协议可以当做一个类型来时候 , 调用调用协议的方法调用的是其实现类的方法
3、可选协议
有时候我们需要在协议中定义的方法 , 有得遵循者需要实现 , 有得不是必须去实现的。这时候就可以使用可选类型。
可选协议只能在含有 @objc
前缀的协议中生效,而且可选协议只能被类去实现 。
@objc protocol Op{
optional var name:String{set get} //可选类型
optional func play()
var age:Int{get}
}
这个协议定义了一个可选属性和一个可选方法还有一个age属性 , 在实现他得类中只有age是必须实现的 。其他两个都是可选的 。
4、委托(代理)模式
protocol SayHelloDelegate{
func sayHello(name:String);
}
class ClassA {
var delegate:SayHelloDelegate?
var name = "lucy"
func play(){
delegate?.sayHello(name);
}
}
class ClassB:SayHelloDelegate {
var name="lily"
func sayHello(name:String) {
print("\(name) 请 \(self.name) 帮她 say Hello");
}
}
我们这里定义了一个协议 ,有个很简单的方法,sayHello
,在A中声明了这个协议变量 ,使用了其方法 ,但这个方法却交给B去实现 。这就是一个很简单的代理模式
var ca = ClassA();
var cb = ClassB()
ca.delegate = cb
//B代理A去实现方法
ca.play(); //lucy 请 lily 帮她 say Hello
这是一个很简单的例子 ,但这个模式一会肯定会有很复杂的用法
5、协议中添加扩展
扩展用extension
关键字 ,后面应该会加上这个的讲解
- 对类扩展实现别的协议
protocol HTMLFormat{
func asHtml(str:String)->String
}
//可以对ClassB扩展实现协议
extension ClassB:HTMLFormat{
func asHtml(str: String)->String {
return "<html>\(str)</html>"
}
}
这里我们让我们前面看得ClassB
再次实现这个协议 。然后他就一共实现了两个协议 , 一个sayHello 一个 hmtl的
var cb1 = ClassB();
cb1.asHtml("dog") //<html>dog</html>
cb1.sayHello("aaa") //aaa 请 lily 帮她 say Hello
这时候这两个方法就都可以用了
当然我们的协议也是可以继承的 。这里就不再示例。
- 类专属协议
protocol C : class{
func say();
}
class D: C{
func say(){
print("hello word")
}
}
看一看知道怎么回事就行了,就是在结构体和枚举里面不能用得协议
- 协议合成
protocol N{
var age:Int{set get}
}
class F:M,N {
var name = "zhangsan"
var age = 18
}
这里我们定义了两个协议 ,F 遵循了这两个协议 ,这时候 我们有个方法需要传入的参数类型是遵循了这两个协议的实例
func happy(mn:protocol<M,N>){
print("\(mn.name) 今年 \(mn.age) 岁了,非常开心!!")
}
var f=F()
happy(f) //zhangsan 今年 18 岁了,非常开心!!
这里F是满足这两个协议的
- 检查协议的一致性
你可以使用 is
as?
as
来检测摸个实例是否遵循某个协议
print(f is M) //true
let c = f as M
print(c) //F
关于is
,as
等得用法 可以看上一节类型转换
- 协议扩展
extension M{
func sayGoodBye(){
print("good-bye")
}
}
在扩展协议中提供默认实现 ,如果在遵循者中实现了此协议,则使用遵循者中的实现