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]