// 協議
/*
OC中也有協議,swift中的協議的作用與OC中基本一樣,只是在寫法上有一點區別。
我們使用 protocol關鍵字來定義一個協議。在一個協議中只能存放計算式屬性以及方法的聲明,
而不能對他們進行定義。
*/
// 1. 協議的定義
// 定義一個協議
protocol MyProt {
/// 聲明一個普通的方法
func foo()
/// 聲明一個可修改存儲式實例屬性的方法
mutating func doSomething(a: Int) -> Int?
/// 聲明一個靜態方法
static func typeMethod()
/// 聲明一個初始化器方法
init(a: Int)
/// 聲明一個下標
subscript(index: Int) -> Int {get set}
/// 聲明一個計算式屬性
var property: Int { get set }
/// 聲明一個計算式屬性,並且是隻讀的
static var typeP: Double { get }
}
// 2. 協議的遵循
/*
只有協議是沒有意義的,協議是用來遵循的,是需要實現的。
swift中,枚舉、類以及結構體類型都可以遵循協議。
遵循了某一協議的類型必須實現該協議中所生命的所有方法與計算式屬性,其中也包括初始化器
*/
do {
struct Test: MyProt {
/// 定義自己的存儲式屬性a
var a = 100
func foo() {
print("foo")
}
mutating func doSomething(a: Int) -> Int? {
print("doSomething")
self.a = a
return a == 0 ? nil : self.a
}
static func typeMethod() {
print("study")
}
init(a: Int) {
self.a = a
}
subscript(index: Int) -> Int {
get {
return a + index
}
set {
a = newValue + index
}
}
var property: Int {
get {
return self.a / 2
}
set {
self.a = newValue / 2
}
}
static var typeP: Double {
return Double.pi
}
}
var t = Test(a: 10)
t.foo()
print("---- \(t.doSomething(a: 100))")
t.property = 12
print("a: \(t.a)")
print("p: \(t.property)")
}
/*
協議是一種比較靈動的動態類型,根據爲它所初始化的對象實例的性質不同,它所採取的拷貝與引用
策略也會有不同。
*/
protocol P {
func foo()
}
do {
print("\n")
struct TestA: P {
var a: Int = 0
func foo() {
print("這是一個foo")
print("a = \(a)")
}
}
/// 定義枚舉類型,遵守協議P
enum TestB: Int, P {
case one = 1, two, three
func foo() {
print("enum = \(self)")
print("value = \(self.rawValue)")
}
}
var a = TestA()
// 聲明P協議類型的對象p,用a對它初始化
var p: P = a
p.foo()
withUnsafePointer(to: a.foo) {
print("\($0)")
}
withUnsafePointer(to: p.foo) {
print("\($0)")
}
withUnsafePointer(to: &a) {
print("p:\($0)")
}
withUnsafePointer(to: &p) {
print("p:\($0)")
}
a.a = 10
p.foo()
withUnsafePointer(to: &a) {
print("\($0)")
}
withUnsafePointer(to: &p) {
print("p:\($0)")
}
/*
打印:
這是一個foo
a = 0
這是一個foo
a = 0
*/
/*
結果說明,p對象不受對象a的影響,爲什麼呢?
因爲結構體和枚舉都是值類型,值類型和引用類型是不一樣的。
執行var p: P = a的時候,系統已經分別給 p開闢了新的空間,所以,改變a,並不會對p造成什麼影響。
*/
p = TestB.two
p.foo()
}
/*
寫時拷貝
由於協議類型是一種抽象類型,swift在實現它的時候採用了一種十分靈活的機制——寫時拷貝。
對於像枚舉、結構體這種值類型的對象實例,即便用一個他們所遵循的協議去指向值類型的對象實例,
當協議類型自身或它所指向的對象實例任一方修改了存儲式實例屬性的值的時候,此時就會發生寫時拷貝。
這時,swift會將協議類型對象分配一個新的存儲空間,然後將它所指向的值類型的對象實例的當前狀態
拷貝過去。
*/
// 一個類型可以遵循多個協議,我們可以用逗號來分割遵循的多個協議的名稱。一個類型若遵循了多個協議
//,那麼它必須實現它所遵循的所有協議中聲明的所有方法和屬性。
protocol ProtA {
func foo()
func method()
var property: Int { get set }
}
protocol ProtB {
mutating func method(a: Int)
static var property: Double { get }
}
do {
print("\n")
struct Test: ProtA, ProtB {
var a = 0
func foo() {
print("協議A的方法,foo")
}
func method() {
print("協議A的方法, method")
}
var property: Int {
get {
return a
}
set {
a = newValue
}
}
mutating func method(a: Int) {
print("協議B的方法,method,a = \(a)")
self.a = a
}
static var property: Double {
return M_E
}
}
let a: ProtA = Test()
a.foo()
a.method()
print("a = \(a.property)")
var b: ProtB = Test()
b.method(a: 10)
print("a = \(type(of: b).property)")
}
// 3. 協議繼承
/*
在swift編程中,一個協議可以繼承自另一個協議或者其他多個協議。當一個協議繼承了其他協議的時候,該
協議會將它所繼承的其他協議中的所有聲明的方法與屬性全部包含在自己的協議之中。
注意:
設計的時候,注意命名。
*/
/// 定義一個協議C,繼承自協議A和協議B
protocol ProtC: ProtA, ProtB {
/// 重載了ProtA的方法
func foo()
/// 自己的方法
func Coo()
}
do {
print("\n")
/// 自定義結構體,遵守了ProtC協議
struct Test: ProtC {
var x = 100
// 以下是要實現的協議方法和屬性
func foo() {
print("我就是個方法,foo")
}
func Coo() {
print("我是C協議的方法,Coo")
}
func method() {
print("我是A的方法,method")
}
var property: Int {
get {
return x
}
set {
x = newValue
}
}
mutating func method(a: Int) {
print("我是B的方法,method, 參數a = \(a)")
x = a
}
static var property: Double {
get {
return 0
}
}
}
var t = Test()
t.foo()
t.Coo()
t.method()
t.method(a: 60)
print("property: \(t.property)")
print("static property: \(Test.property)")
}
// 4. class協議
/*
我們可以將一個協議限定爲只適用於類類型。我們只需要在一個協議後面使用:class 即可將該協議聲明
爲類協議,這樣只有類類型才能遵守該協議。
*/
/// 定義一個協議ProtD,是一個類協議
protocol ProtD: class {
/// 聲明類型計算式屬性
static var type: String { get set }
func hello()
}
/// 定義一個協議ProtE,普通協議
protocol ProtE {
func welcome()
}
/// 定義一個協議ProtF, 是一個類協議,繼承自ProtE
protocol ProtF: class, ProtE {
var property: Int { get set }
}
/// 定義一個協議ProtG,繼承自ProtD和ProtE
protocol ProtG: ProtD, ProtE {
}
do {
print("\n")
class Dog: ProtF {
var name: String = "ww"
/// 使用class覆蓋協議中的類類型屬性
class var type: String {
get {
return "中華田園犬"
}
set {
}
}
var property: Int {
get {
return 10
}
set {
}
}
/// 實現ProtE的方法
func welcome() {
print("歡迎來到狗狗之家")
}
}
let dog = Dog()
dog.name = "小花花"
dog.welcome()
print("這隻小狗已經 \(dog.property) 歲了")
print("這隻狗狗的品種是 \(Dog.type)")
/// 定義了結構體 Cat,並實現ProtE
struct Cat: ProtE {
var name = "mimi"
/// 定義自己的方法
func eat(fishCount: Int) {
print("給貓咪餵了 \(fishCount) 只小魚")
}
/// 實現ProtE協議的方法
func welcome() {
print("你們好,這裏是貓咪的樂園")
}
}
var cat = Cat()
cat.name = "小黃"
cat.welcome()
cat.eat(fishCount: 3)
/*
注意,如果一個協議繼承了某個類協議,那麼他自己也就成了類協議。
如果一個非類類型遵循了一個類協議,那麼就會引發編譯報錯。
*/
}
// 5.協議組合
/*
有時,我們需要聲明一個對象,其類型需要遵循多個協議,以便能夠調用這些協議中的方法或者訪問屬性。
我們可以在定義一個協議,然後繼承我們想要遵循的協議,但是這會顯得很繁瑣。swift中提供了“協議組合”
這一語法特性,使得我們能夠很輕鬆的將所要遵循的協議給組合起來。
*/
protocol P1 {
func foo()
func method()
}
protocol P2 {
mutating func method(a: Int)
var age: Int { get set }
}
do {
print("\n")
// 定義結構體Test,遵循了p1,p2協議
struct Test: P1,P2 {
/// 自己的屬性a
var a = 0
/// 以下是實現協議的方法和屬性
func foo() {
print("P1:這個世界怎麼了,這個世界上的人太瘋狂了")
}
func method() {
print("P1: 還能怎麼辦呢?滄海一粟,宇宙一蜉蝣而已,小小屁民,苟活於世")
}
mutating func method(a: Int) {
print("p2: 好好生活,好好掙錢吧,多掙一點,養活好自己和家人。a: \(a)")
self.a = a
}
var age: Int {
get {
return a
}
set {
self.a = newValue
}
}
}
var t = Test()
t.a = 100
t.foo()
t.method()
t.method(a: 50)
print("age: \(t.age)")
t.age = 102
print("向天再借: \(t.age) 年")
/// 聲明一個對象p,遵循了P1和P2,這裏就使用了協議組合的語法特性。
var p: P1 & P2 = Test()
p.foo()
p.age = 100
p.method(a: 6)
print("age = \(p.age)")
}
// 6.關聯類型
/*
我們在協議中聲明一些方法,這些方法的參數類型根據不同的實現可能會有所不同,此時swift提供了協議的
“關聯類型”語法特性,使得我們可以在協議中聲明泛化的抽象類型,每個遵循它的類型可以指定其自己的
具體類型。
*/
protocol P6A {
/// 聲明瞭關聯類型DataType
associatedtype DataType
/// 聲明瞭實例方法method,具有一個關聯類型參數value
func method(value: DataType)
}
do {
print("\n")
struct MyStruct:P6A {
/// 通過typealias指定協議關聯類型的具體類型,這裏把DataType指定爲Int類型
typealias DataType = Int
let data: DataType = 100
func method(value: Int) {
print("哈哈哈哈,這裏是關聯類型的測試場地: \(data + value)")
}
}
/// 在定義一個枚舉類型
enum MyEnum: String, P6A {
// 這裏把DataType指定爲String類型
typealias DataType = String
case one = "1", two = "2", three = "3"
func method(value: String) {
print("這裏是一個枚舉,\(self.rawValue + value)")
}
}
// 定義一個函數test
// 泛型 T 必須遵循P6A協議
func test<T>(prot: T, param: T.DataType) where T: P6A {
prot.method(value: param)
}
test(prot: MyStruct(), param: -1)
test(prot: MyEnum.two, param: "apple")
}
// 7.關於協議中的self類型
/*
在swift編程語言的協議中有一個內建的關聯類型——self,用於指代遵循該協議的具體類型。
*/
protocol P7A {
mutating func method(obj: Self)
}
do {
print("\n")
struct MyStruct:P7A {
var data = 1
mutating func method(obj: MyStruct) {
data += obj.data
}
}
enum MyEnum: String, P7A {
case red = "red", green = "green",blue = "blue"
case red_and_green = "red and green"
case red_and_blue = "red and blue"
case green_and_blue = "green and blue"
mutating func method(obj: MyEnum) {
guard let newValue = MyEnum(rawValue: self.rawValue + "and" + obj.rawValue) else {
return
}
self = newValue
}
}
func test<T>(dst: inout T, src: T) where T: P7A {
dst.method(obj: src)
}
var sd = MyStruct()
let ss = MyStruct(data: 100)
test(dst: &sd, src: ss)
print("sd = \(sd.data)")
var ed = MyEnum.red
let es = MyEnum.green
test(dst: &ed, src: es)
print("ed = \(ed)")
}