Swift学习笔记-009协议(疯狂swift讲义第二版)

1.协议的语法(十分类似java的接口)

修饰符 protocol 协议名:父协议1,父协议2,…{
//协议内容
}

2.协议的使用

结构体使用协议
struct 结构体名:协议1,协议2…{
//实现协议
}

class 类名:父类,协议1,协议2,…{
//类语句及实现协议
}

3.协议中添加属性

一般格式
[static] var 属性名:类型{get [set]}
static定义类属性,这里不可以用class替代
get,set只需填写即可,不需要写其实现,set可有可无,如果有set则要求实现者提供与之匹配的读写属性

4.结构体和类中使用协议
protocol Strokable {
    var strokeWidth:Double{get set}
}

enum Color{
    case Red,Green,Blue,Yellow,Cyan
}
protocol Fullable {
    var fullColor:Color?{get set}
}

protocol HasArea:Fullable,Strokable {
    var area:Double {get}
}
protocol Mathable {
    static var pi:Double{get}
    static var e:Double{get}
}

//实现以上接口

struct Rect:HasArea,Mathable{
    var fullColor: Color?
    var strokeWidth: Double=0.0
    //+++++++++++++++++++
    
    var area: Double{
        get{ return width*height}
    }
    //+++++++++++++++++++
    
    static var pi: Double=3.1415926
    static var e: Double=2.718
    //+++++++++++++++++++
    var width:Double
    var height:Double
    init(width:Double,height:Double) {
        self.width=width
        self.height=height
    }
    
}

class Circle:HasArea,Mathable{
    var radius:Double
    init(radius:Double) {
        self.radius=radius
    }
    var fullColor: Color?
    var strokeWidth: Double=0.0
    var area: Double{
        get{
            return Circle.pi*radius*radius//实现协议是,这个变量被继承下来,接下来也将多这个属性赋值,所以应该使用Circle.pi而不是使用Mathable.pi。如果使用Mathable.pi则会报错误:Static member 'pi' cannot be used on protocol metatype 'Mathable.Protocol'
        }
    }
    static var pi:Double=3.1415926
    static var e:Double=2.714
}

var rect = Rect(width: 4.5, height: 4.0)
print(rect.area)
rect.fullColor=Color.Red
rect.strokeWidth=1.0
print(rect.fullColor!)
print(rect.strokeWidth)

var circle = Circle(radius: 2.0)
circle.fullColor=Color.Green
circle.strokeWidth=0.5
print(circle.fullColor!)
print(circle.strokeWidth)

打印结果
18.0
Red
1.0
Green
0.5

5.协议中的方法

支持形参可变的方法

protocol Eatable {
    func taste()
    static func test(msg:String...)
}
//结构体实现协议
struct Pie:Eatable{
    var weight:Double
    func taste() {
        print("\(weight) 重量的饼干才能吃饱")
    }
    
    static func test(msg: String...) {
        print("实现关于饼干的类方法")
        for mymsg in msg{
            print("here is the para \(mymsg)")
        }
    }

}
//类实现协议
class Apple: Eatable {
  
    
    var name:String
    init(name:String) {
        self.name=name
    }
    func taste() {
        print("\(name) is full of water")
    }
    static func test(msg: String...) {
        for mymsg in msg{
            print("个数可变的形参\(mymsg)")
        }
    }
}

//执行方法
Pie.test(msg: "饼干1","饼干2","饼干3")
var pie=Pie(weight: 2.3)
pie.taste()
Apple.test(msg: "冰糖心苹果","红富士苹果","其他苹果")
var apple=Apple(name: "本地苹果")
apple.taste()

打印结果
实现关于饼干的类方法
here is the para 饼干1
here is the para 饼干2
here is the para 饼干3
2.3 重量的饼干才能吃饱
个数可变的形参冰糖心苹果
个数可变的形参红富士苹果
个数可变的形参其他苹果
本地苹果 is full of water

5.协议中的可变方法
protocol Incrementable {
    mutating func incremetByDelta(delta:Double)
}
//使用结构体实现接口
struct FKRange:Incrementable{
    var start:Double
    var length:Double
    mutating func incremetByDelta(delta:Double){
        self.length+=delta
    }
}
//使用类实现接口
class Circle:Incrementable{
    var radius:Double
    init (radius:Double){
        self.radius=radius
    }
    func incremetByDelta(delta:Double){
        self.radius+=delta
    }
}

//执行
var range = FKRange(start: 2.0, length: 10.0)
range.incremetByDelta(delta: 5.0)
print(range.length)
var circle=Circle(radius: 5.0)
circle.incremetByDelta(delta: 5.0)
print(circle.radius)

打印结果
15.0
10.0

6.协议指定的下标

subscript(形参列表)->返回值{get [set]}

protocol Mathable {
    subscript (idx:Int)->Int{get}
    subscript(a:Int,b:Int)->Int{get}
}

//结构体实现协议
struct LinearStruct:Mathable{
    var factor:Int
    subscript(idx:Int)->Int{
        get{
            return factor*idx
        }
        set{
            print("执行LinearStruct的下标赋值")
        }
    }
    subscript(a:Int,b:Int)->Int{
        return factor*a+b
    }
}

//类实现协议
class Quadratic: Mathable {
    var factor:Int
    init(factor:Int){
        self.factor=factor
    }
    //实现下标
    subscript(idx:Int)->Int{
        return factor*factor*idx
    }
    subscript(a:Int,b:Int)->Int{
        return factor*factor*a+b
    }
}

//执行
var q=Quadratic(factor: 5)
print(q[4])
print(q[4,6])

var line=LinearStruct(factor: 5)
print(line[4])
print(line[4,6])

打印结果
100
106
20
26

7.协议中指定构造器

类实现协议时,可以使用指定构造器也可以是用便利构造器,此外还需注意两点
1、使用类实现协议,并实现构造器,必须使用required修饰符,除非构造器之前使用了final
2、如果类在实现协议并实现构造器时,如果子类重写了父类的构造器,必须同时使用required override修饰

protocol Initable{//接口的命名好像都喜欢用able
    //定义两个构造器
    init(name:String)
    init(name:String,weight:Double)
}
//d使用结构体实现协议
struct Bag:Initable{
    var name:String
    var weight:Double
    init(name:String){
        self.name=name
        self.weight=0.0
    }
    init(name: String, weight: Double) {
        self.name=name
        self.weight=weight
    }
}

//使用类实现协议
//先定义一个父类
class Fruit{
    var name:String
    init(name:String) {
        self.name=name
    }
}
//在子类中实现协议
class Apple:Fruit,Initable{
    var weight:Double
    //使用required override
    required override init(name: String) {
        self.weight=0.0
        super.init(name: name)
    }
    //使用便利构造器实现协议的构造器
    required convenience init(name: String, weight: Double) {
        self.init(name: name)
        self.weight=weight

    }
}

//执行
var apple1 = Apple(name: "红富士")
var apple2=Apple(name: "冰糖心", weight: 2.3)
print("\(apple2.name)----\(apple2.weight)")

var bag1=Bag(name: "书包")
var bag2=Bag(name: "旅行包", weight: 20.3)
print("\(bag2.name)----\(bag2.weight)")

打印结果
冰糖心----2.3
旅行包----20.3

8.使用协议作为类型

使用协议声明变量
协议作为函数,方法,构造器的形参、返回值类型
作为泛型参数
作为is、as、as?、as!等运算符的后一个操作数

protocol Eatable {
    func taste()
    static func test(msg:String...)
}
//结构体实现协议
struct Pie:Eatable{
    var weight:Double
    func taste() {
        print("\(weight) 重量的饼干才能吃饱")
    }
    
    static func test(msg: String...) {
        print("实现关于饼干的类方法")
        for mymsg in msg{
            print("here is the para \(mymsg)")
        }
    }
    
}
//类实现协议
class Apple: Eatable {
    
    
    var name:String
    init(name:String) {
        self.name=name
    }
    func taste() {
        print("\(name) is full of water")
    }
    static func test(msg: String...) {
        for mymsg in msg{
            print("个数可变的形参\(mymsg)")
        }
    }
}
//将Apple和Pie的实例赋值给food,这是向上转型
var food1:Eatable=Apple(name: "红富士")
var food2:Eatable=Pie(weight: 1.2)
food1.taste()
food2.taste()
//协议实例作为形参
func eat(foods:Eatable...){
    for food in foods{
        food.taste()
    }
}
//即可将Apple类实例也可将Pie类实例传入作为形参,应为形参要求是Eatable类型,这两者都是Eatable协议的实现类
eat(foods: Apple(name: "红富士"),Pie(weight: 2.3))

//使用Eatable协议类型作为数组类型
var foodArray:[Eatable] = [Apple(name: "红富士"),Apple(name: "冰糖心"),Pie(weight: 2.3),Pie(weight: 3.4)]
for food in foodArray{
    if let ap=food as?Apple{
        print(ap.name)
    }else if let pie=food as?Pie{
        print(pie.weight)
    }
}

打印结果:
红富士 is full of water
1.2 重量的饼干才能吃饱
红富士 is full of water
2.3 重量的饼干才能吃饱
红富士
冰糖心
2.3
3.4

9.合成协议(多个协议合在一起)

一般性格式
protocol (协议1 &协议2 &协议3 &…)//swift4.0中已经是用&符号替代协议之间的逗号(,)

protocol Strokable {
    var strokeWidth:Double{get set}
}

enum Color{
    case Red,Green,Blue,Yellow,Cyan
}
protocol Fullable {
    var fullColor:Color?{get set}
}

protocol HasArea:Fullable,Strokable {
    var area:Double {get}
}
protocol Mathable {
    static var pi:Double{get}
    static var e:Double{get}
}
struct Rect:HasArea,Mathable{
    var fullColor: Color?
    var strokeWidth: Double=0.0
    //+++++++++++++++++++
    
    var area: Double{
        get{ return width*height}
    }
    //+++++++++++++++++++
    
    static var pi: Double=3.1415926
    static var e: Double=2.718
    //+++++++++++++++++++
    var width:Double
    var height:Double
    init(width:Double,height:Double) {
        self.width=width
        self.height=height
    }
    
}

class Circle:HasArea,Mathable{
    var radius:Double
    init(radius:Double) {
        self.radius=radius
    }
    var fullColor: Color?
    var strokeWidth: Double=0.0
    var area: Double{
        get{
            return Circle.pi*radius*radius//实现协议是,这个变量被继承下来,接下来也将多这个属性赋值,所以应该使用Circle.pi而不是使用Mathable.pi。如果使用Mathable.pi则会报错误:Static member 'pi' cannot be used on protocol metatype 'Mathable.Protocol'
        }
    }
    static var pi:Double=3.1415926
    static var e:Double=2.714
}

//合成协议
func test(arg:HasArea & Mathable){
    print("arg参数的填充颜色是\(arg.fullColor)")
    print("arg参数描边的粗细是\(arg.strokeWidth)")
    print("arg参数的面积是\(arg.area)")
}

var circle=Circle(radius:1.2)
circle.strokeWidth=0.5
circle.fullColor=Color.Red

var rect=Rect(width: 3.4, height: 2.8)
rect.strokeWidth=0.8
rect.fullColor=Color.Green
//test函数必须同时实现HasArea、Mathable协议,因此可以传入Circle,Rect实例
test(arg: circle)
test(arg: rect)

打印结果
arg参数的填充颜色是Optional(MyEX011.Color.Red)
arg参数描边的粗细是0.5
arg参数的面积是4.523893343999999
arg参数的填充颜色是Optional(MyEX011.Color.Green)
arg参数描边的粗细是0.8
arg参数的面积是9.52

10.通过扩展为已有的类型添加协议
protocol Eatable {
    func taste()
}
//通过扩展让String实现Eatable
extension String:Eatable{
    func taste(){
        print("\(self)吃起来味道不错")
    }
}
func eat(foods:Eatable...){
    for food in foods {
        food.taste()
    }
}

eat(foods: "花生","瓜子","八宝粥")

打印结果
花生吃起来味道不错
瓜子吃起来味道不错
八宝粥吃起来味道不错

如果已有类型敲好实现了协议的要求,则可以更简单地通过扩展让该类型实现这个协议,此时扩展的花括号内不需要书写任何代码

protocol Emptytable{
    var isEmpty:Bool{get}
}
//通过扩展让String补充实现Emptytable协议
//由于String已经实现了Emptable协议的方法,因此扩展中无须任何代码
extension String:Emptytable{}
//定义一个方法,该方法需要EMptable参数
func foo(arg:Emptytable){
    print("arg是否为空:\(arg.isEmpty)")
}
foo(arg: "hello world")
foo(arg: "")
11.唯类(class-only)协议(只能被类实现的协议)

一般格式:
protocal 协议名:class,父协议1,父协议2,…{
//协议的定义
}

protocol Movable:class{//定义一个唯类协议
    func move()
}
class Car:Movable{
    func move() {
        print("we are moving")
    }
}
var car=Car()
car.move()

打印结果
we are moving

12.可选协议

为了和oc协议兼容,可选协议签必须添加@objc修饰符并且在协议成员前添加optional关键字即可定义可选协议
swift4要求每个协议成员之前都要加@objc

@objc protocol MyProtocol{
    @objc optional var status:String{get}
    @objc optional func increment(val:Int)
    @objc optional subscript(idx:Int)->Int{get}
}
//由于是可选的协议,所以可以完全不实现,定义一个空类
class EmptyClass:NSObject,MyProtocol{

}
@objc class Myclass:NSObject,MyProtocol{
    var name:String
    init(name:String){
        self.name=name
    }
    //实现可选协议的一个属性
    var status: String{
        if name.utf16.count<10{
            return "良好"
        }else{
            return "超长"
        }
    }
    //实现可选协议的方法
    func increment(val: Int) {
        print("系统正在增长长度")
        for _ in 1...val{
            name+="="
        }
    }
}

var mp:MyProtocol=Myclass(name: "张三丰")
print(mp.status)
print(mp[4])
mp.increment?(val: 10)//这里是用了?可选链
print(mp.status)

var ec:MyProtocol=EmptyClass()
print(ec.status)
print(ec[4])
ec.increment?(val: 10)//这里是用了?可选链
12.协议扩展
protocol Eatable {
     func taste()
    static func test(msgs:String...)
}
//扩展协议,在扩展部分,甚至提供了部分的实现
extension Eatable{
    var type:String{
        return "食物"
    }
    func info(){
        print("至少可以吃")
    }
    subscript (idx:Int)->Int{
        return idx*idx
    }
    /*可以在扩展里面直接实现接口的类方法,提供默认的实现
    static func test(msgs: String...) {
        print("通过扩展实现的默认test方法")
        for msg in msgs{
            print("个数可变的形参:\(msg)")
        }
    }
    */
}
//类实现Eatable协议
class Apple:Eatable{
    var name:String
    init(name:String){
        self.name=name
    }
    //实现协议中实例方法
    func taste(){
        print("\(name)水分充足,营养丰富")
    }
    //实现协议中的类方法,使用class修饰符修饰
    class func test(msgs: String...) {
        print("苹果实现的test方法")
        for msg in msgs{
            print("个数可变的形参:\(msg)")
        }
    }
}


//结构体实现Eatable协议
struct Pie:Eatable {
    var weight:Double
    init(weight:Double){
        self.weight=weight
    }
    
    static func test(msgs: String...) {
        print("苹果实现的test方法")
        for msg in msgs{
            print("个数可变的形参:\(msg)")
        }
    }
    
    func taste(){
        print("\(weight)公斤饼干刚刚能吃饱")
    }

}

let pie:Pie=Pie(weight: 2.3)
pie.taste()
print(pie.type)//扩展来的属性
pie.info()//扩展来的方法
print(pie[3])
Apple.test(msgs: "红富士","冰糖心")
Pie.test(msgs:"吉百利","馅饼")

打印结果:
2.3公斤饼干刚刚能吃饱
食物
至少可以吃
9
苹果实现的test方法
个数可变的形参:红富士
个数可变的形参:冰糖心
苹果实现的test方法
个数可变的形参:吉百利
个数可变的形参:馅饼

12.输出实例和CustomStringConvertible

如果希望输出实例时,能看到实物的内部状态,可以让该类实现CustomStringConvertible类协议,并实现该协议中的description只读属性

class Person:CustomStringConvertible{
    var name:String
    var age:Int
    init(name:String,age:Int) {
        self.name=name
        self.age=age
    }
    var description: String{
        return "name=\(name)---\(age)"
        
    }
}

var p1=Person(name: "张三丰", age: 100)
print(p1)
print(p1.description)

打印结果
name=张三丰—100
name=张三丰—100

13.使用自定义类型作为字典的key(==运算符重载)(实现Equatable协议)

字典的两个key相等,需要满足
1、两个key通过= =比较返回true
2、两个key的hashvalue属性返回相等的整数

自定义类型实例的比较需要
1、让自定义类型实现Equatbale协议,并重载“= =”比较运算符,使得自定义类型的实例可以通过“= =”进行比较
2、让自定义类型实现Hashable协议,并实现该协议的hashValue只读属性。实现Hashable只读属性时,应和重载的==保持一致。

class User:Equatable{
    var name:String
    var password:String
    var age:Int
    init(name:String,password:String,age:Int){
        self.name=name
        self.password=password
        self.age=age
    }
}
//重载= =运算符
//(实际上就是重新定义名为= =的函数,但形参列表与已有的= =函数形参列表并不相同)
func = = (lhs:User,rhs:User)->Bool{
    //当两个User实例的name,password都相等时,即认为两个User实例相等
    return lhs.name==rhs.name && lhs.password == lhs.password
}
var u1=User(name: "zhangsanfeng", password: "123456", age: 23)
var u2=User(name: "zhangsanfeng", password: "123456", age: 34)
print(u1==u2)
var u3=User(name: "luoyulong", password: "123456", age: 34)
print(u1==u3)
print(u2 != u3)

打印结果
true
false
true

struct User:Hashable,CustomStringConvertible {
    var name:String
    var password:String
    var age:Int
    init(name:String,password:String,age:Int){
        self.name=name
        self.password=password
        self.age=age
    }
    var hashValue:Int{
        return name.hashValue &* 31 &+ password.hashValue//考虑到数据可能溢出,故此处采用溢出运算符
    }
    var description: String{
        return "\(name)--\(password)--\(age)"
    }
}
//重载= =运算符
func == (lhs:User,rhs:User)->Bool{
    return lhs.name==rhs.name && lhs.password==lhs.password
}

var dict=[User:Int]()
dict[User(name: "zhangsanfeng", password: "123", age: 100)]=50
dict[User(name: "yuebuqu", password: "133", age: 34)]=220
dict[User(name: "zhangsanfeng", password: "123", age: 43)]=100
print(dict)

打印结果
[zhangsanfeng–123–43: 100, yuebuqu–133–34: 220]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值