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]