Swift 协议

プロトコルは、特定の機能を実装するために必要なメソッドとプロパティを規定します。

プロトコルの要件を満たす任意の型は、そのプロトコルに準拠(conform)していると呼ばれます。

クラス、構造体、列挙型はすべてプロトコルに準拠でき、プロトコルが定義するメソッドと機能を実現する具体的な実装を提供できます。

Swift のプロトコル(Protocols)は、特定のタスクや機能に適用されるメソッド、プロパティ、その他の要件を定義する青写真です。プロトコルはクラス、構造体、列挙型が採用し、これらの要件の具体的な実装を提供できます。

プロトコルは Swift 言語の非常に強力な機能であり、開発者がインターフェースを定義し、ある型が必ず持つべきプロパティとメソッドを指定することを可能にします。その際、それらのプロパティとメソッドの具体的な実装を指定する必要はありません。

语法

プロトコルの構文形式は次の通りです。

protocol SomeProtocol {
    // 这里可以定义协议要求
    var someProperty: Int { get set }
    func someMethod()
}

SomeProtocol は読み書き可能なプロパティ someProperty とメソッド someMethod を定義しています。

クラスをあるプロトコルに準拠させるには、型名の後にプロトコル名を追加し、その間をコロンで:区切り、型定義の一部とします。複数のプロトコルに準拠する場合は、各プロトコルをカンマで区切ります。,分隔。

struct SomeStructure: FirstProtocol, AnotherProtocol {
    // 结构体内容
}

クラスがプロトコルに準拠しながら親クラスを持つ場合は、親クラス名をプロトコル名の前に置き、カンマで区切ります。

class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
    // 类的内容
}

プロパティに対する要件

プロトコルは、特定のインスタンスプロパティまたはクラスプロパティを指定するために使用され、それが格納型プロパティか計算型プロパティかを指定する必要はありません。さらに、読み取り専用か読み書き可能かを明示する必要もあります。

プロトコル内では通常、変数プロパティの宣言には var を使用し、型宣言の後に付け加えます。{ set get }これは、プロパティが読み書き可能であることを示します。読み取り専用プロパティの場合は{ get }来表示。

实例

protocol ClassA {
    var marks: Int { get set }
    var result: Bool { get }
   
    func attendance() -> String
    func markssecured() -> String
}

protocol ClassB: ClassA {
    var present: Bool { get set }
    var subject: String { get set }
    var stname: String { get set }
}

class ClassC: ClassB {
    var marks = 96
    let result = true
    var present = false
    var subject = "Swift 协议"
    var stname = "Protocols"
   
    func attendance() -> String {
        return "The \(stname) has secured 99% attendance"
    }
   
    func markssecured() -> String {
        return "\(stname) has scored \(marks) marks"
    }
}

let studdet = ClassC()
studdet.stname = "Swift"
studdet.marks = 98

print(studdet.markssecured())
print(studdet.attendance())
print("Marks: \(studdet.marks)")
print("Result: \(studdet.result)")
print("Present: \(studdet.present)")
print("Subject: \(studdet.subject)")
print("Student Name: \(studdet.stname)")

上記のプログラムを実行すると、出力結果は次のようになります:

Swift has scored 98 marks
The Swift has secured 99% attendance
Marks: 98
Result: true
Present: false
Subject: Swift 协议
Student Name: Swift

Mutating メソッドに対する要件

メソッド内でそのインスタンスを変更する必要がある場合があります。

例えば、値型(構造体、列挙型)のインスタンスメソッドでは、mutatingキーワードを関数の接頭辞としてfuncの前に記述することで、そのメソッドが所属するインスタンスおよびそのインスタンスプロパティの値を変更できることを示します。

protocol Daysofaweek {
    mutating func show()
}

enum Days: Daysofaweek {
    case sun, mon, tue, wed, thurs, fri, sat

    mutating func show() {
        switch self {
        case .sun:
            self = .sun
            print("Sunday")
        case .mon:
            self = .mon
            print("Monday")
        case .tue:
            self = .tue
            print("Tuesday")
        case .wed:
            self = .wed
            print("Wednesday")
        case .thurs:
            self = .thurs
            print("Thursday")
        case .fri:
            self = .fri
            print("Friday")
        case .sat:
            self = .sat
            print("Saturday")
        }
    }
}

var res = Days.wed
res.show()

上記のプログラムを実行すると、出力結果は次のようになります:

Wednesday

イニシャライザに対する要件

プロトコルは、それに準拠する型に対して、指定されたイニシャライザの実装を要求できます。

通常のイニシャライザを記述するのと同じように、プロトコルの定義内にイニシャライザの宣言を記述できます。ただし、波括弧とイニシャライザの本体を記述する必要はありません。構文は次のとおりです:

protocol SomeProtocol {
   init(someParameter: Int)
}

实例

protocol tcpprotocol {
   init(aprot: Int)
}

プロトコルのイニシャライザは、クラス内での実装を規定します。

そのプロトコルに準拠するクラス内でイニシャライザを実装し、それをクラスの指定イニシャライザまたは便利イニシャライザとして指定できます。どちらの場合でも、イニシャライザの実装に "required" 修飾子を付けなければなりません:

class SomeClass: SomeProtocol {
   required init(someParameter: Int) {
      // 构造器实现
   }
}

protocol tcpprotocol {
   init(aprot: Int)
}

class tcpClass: tcpprotocol {
   required init(aprot: Int) {
   }
}

required 修飾子を使用すると、そのプロトコルに準拠するすべてのサブクラスが、イニシャライザの要件に対して明示的な実装または継承された実装を提供できることが保証されます。

サブクラスが親クラスの指定イニシャライザをオーバーライドし、そのイニシャライザが何らかのプロトコルの要件に準拠している場合、そのイニシャライザの実装には required と override の両方の修飾子を付ける必要があります:

protocol TcpProtocol {
    init(no1: Int)
}

class MainClass {
    var no1: Int // 局部变量
    init(no1: Int) {
        self.no1 = no1 // 初始化
    }
}

class SubClass: MainClass, TcpProtocol {
    var no2: Int
    
    init(no1: Int, no2: Int) {
        self.no2 = no2
        super.init(no1: no1)
    }
    
    // 因为遵循协议,需要加上"required"; 因为继承自父类,需要加上"override"
    required override convenience init(no1: Int) {
        self.init(no1: no1, no2: 0)
    }
}

let res = MainClass(no1: 20)
let show = SubClass(no1: 30, no2: 50)

print("res is: \(res.no1)")
print("show.no1 is: \(show.no1)")
print("show.no2 is: \(show.no2)")

上記のプログラムを実行すると、出力結果は次のようになります:

res is: 20
show.no1 is: 30
show.no2 is: 50

协议类型

プロトコル自体は機能を実装しませんが、プロトコルは型として使用できます。

プロトコルは他の通常の型と同様に使用できます。使用場面は次のとおりです:

  • 関数、メソッド、またはイニシャライザのパラメータ型や戻り値型として
  • 定数、変数、またはプロパティの型として。
  • 配列、辞書、その他のコンテナの要素型として

实例

protocol Generator {
    associatedtype Members
    func next() -> Members?
}

// 示例代码使用 Swift 标准库中的迭代器和 `map` 函数

// 使用数组的迭代器
var items = [10, 20, 30].makeIterator()
while let x = items.next() {
    print(x)
}

// 使用 `map` 函数
for list in [1, 2, 3].map({ i in i * 5 }) {
    print(list)
}

// 直接打印数组
print([100, 200, 300])
print([1, 2, 3].map({ i in i * 10 }))

上記のプログラムを実行すると、出力結果は次のようになります:

10
20
30
5
10
15
[100, 200, 300]
[10, 20, 30]

拡張でプロトコルメンバーを追加する。

拡張によって、既存の型(クラス、構造体、列挙型など)を拡張することができます。

拡張は、既存の型にプロパティ、メソッド、サブスクリプト、プロトコル準拠などのメンバーを追加できます。

protocol AgeClassificationProtocol {
    var age: Int { get }
    func agetype() -> String
}

class Person {
    let firstname: String
    let lastname: String
    var age: Int

    init(firstname: String, lastname: String, age: Int = 10) {
        self.firstname = firstname
        self.lastname = lastname
        self.age = age
    }
}

extension Person: AgeClassificationProtocol {
    func fullname() -> String {
        return "\(firstname) \(lastname)"
    }

    func agetype() -> String {
        switch age {
        case 0...2:
            return "Baby"
        case 3...12: // 修正为从3开始
            return "Child"
        case 13...19:
            return "Teenager"
        case let x where x > 65:
            return "Elderly"
        default:
            return "Normal"
        }
    }
}

// 测试代码
let person1 = Person(firstname: "John", lastname: "Doe", age: 25)
let person2 = Person(firstname: "Jane", lastname: "Smith", age: 70)
let person3 = Person(firstname: "Baby", lastname: "Yoda", age: 1)

print("\(person1.fullname()) is a \(person1.agetype())")
print("\(person2.fullname()) is a \(person2.agetype())")
print("\(person3.fullname()) is a \(person3.agetype())")

上記のコードを実行すると、出力結果は次のようになります:

John Doe is a Normal
Jane Smith is a Elderly
Baby Yoda is a Baby

プロトコルの継承

プロトコルは1つ以上の他のプロトコルを継承でき、継承したプロトコルに基づいて新しい要件を追加できます。

プロトコルの継承構文はクラスの継承に似ており、複数の継承されるプロトコルはカンマで区切ります:

protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
    // 协议定义
}

实例

protocol Classa {
    var no1: Int { get set }
    func calc(sum: Int)
}

protocol Result {
    func print(target: Classa)
}

class Student2: Result {
    func print(target: Classa) {
        target.calc(sum: 1)
    }
}

class Classb: Result {
    func print(target: Classa) {
        target.calc(sum: 5)
    }
}

class Student: Classa {
    var no1: Int = 10
    
    func calc(sum: Int) {
        no1 -= sum
        print("学生尝试 \(sum) 次通过")
        
        if no1 <= 0 {
            print("学生缺席考试")
        }
    }
}

class Player {
    var stmark: Result!
    
    init(stmark: Result) {
        self.stmark = stmark
    }
    
    func print(target: Classa) {
        stmark.print(target: target)
    }
}

var marks = Player(stmark: Student2())
var marksec = Student()

marks.print(target: marksec)
marks.print(target: marksec)
marks.print(target: marksec)
marks.stmark = Classb()
marks.print(target: marksec)
marks.print(target: marksec)
marks.print(target: marksec)

上記のプログラムを実行すると、出力結果は次のようになります:

学生尝试 1 次通过
学生尝试 1 次通过
学生尝试 1 次通过
学生尝试 5 次通过
学生尝试 5 次通过
学生缺席考试
学生尝试 5 次通过
学生缺席考试

类专属协议

プロトコルの継承リストに class キーワードを追加することで、プロトコルをクラス(class)型にのみ適合するように制限できます。

その class キーワードは、プロトコルの継承リストの最初に記述されている必要があり、その後に他の継承プロトコルが続きます。形式は次のとおりです:

protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
    // 协议定义
}

实例

protocol TcpProtocol {
    init(no1: Int)
}

class MainClass {
    var no1: Int // 局部变量
    init(no1: Int) {
        self.no1 = no1 // 初始化
    }
}

class SubClass: MainClass, TcpProtocol {
    var no2: Int

    init(no1: Int, no2: Int) {
        self.no2 = no2
        super.init(no1: no1)
    }
    
    // 因为遵循协议,需要加上"required"; 因为继承自父类,需要加上"override"
    required override convenience init(no1: Int) {
        self.init(no1: no1, no2: 0)
    }
}

let res = MainClass(no1: 20)
let show = SubClass(no1: 30, no2: 50)

print("res is: \(res.no1)")
print("show.no1 is: \(show.no1)")
print("show.no2 is: \(show.no2)")

上記のプログラムを実行すると、出力結果は次のようになります:

res is: 20
show.no1 is: 30
show.no2 is: 50

协议合成

Swift は複数のプロトコルの合成をサポートしており、複数のプロトコルに同時に準拠する必要がある場合に非常に便利です。

構文は次のとおりです:

protocol Stname {
    var name: String { get }
}

protocol Stage {
    var age: Int { get }
}

struct Person: Stname, Stage {
    var name: String
    var age: Int
}

func show(celebrator: Stname & Stage) {
    print("\(celebrator.name) is \(celebrator.age) years old")
}

let studname = Person(name: "Priya", age: 21)
show(celebrator: studname)

let stud = Person(name: "Rehan", age: 29)
show(celebrator: stud)

let student = Person(name: "Roshan", age: 19)
show(celebrator: student)

上記のプログラムを実行すると、出力結果は次のようになります:

Priya is 21 years old
Rehan is 29 years old
Roshan is 19 years old

プロトコル準拠の確認

is 演算子と as 演算子を使用して、あるプロトコルに準拠しているかどうかをチェックしたり、ある型に強制変換したりできます。

  • is演算子は、インスタンスが〜かどうかを確認するために使用されます準拠ある特定のプロトコル。
  • as?オプショナル値を返し、インスタンスが...準拠プロトコルに準拠している場合は、そのプロトコル型を返し、そうでない場合はnil。
  • as強制ダウンキャストに使用され、強制キャストが失敗した場合、実行時エラーが発生します。

实例

次の例では、Double 型の読み取り可能な area を要求する HasArea プロトコルを定義しています:

protocol HasArea {
    var area: Double { get }
}

// 定义了Circle类,都遵循了HasArea协议
class Circle: HasArea {
    let pi = 3.1415927
    var radius: Double
    var area: Double { return pi * radius * radius }
    init(radius: Double) { self.radius = radius }
}

// 定义了Country类,都遵循了HasArea协议
class Country: HasArea {
    var area: Double
    init(area: Double) { self.area = area }
}

// Animal是一个没有实现HasArea协议的类
class Animal {
    var legs: Int
    init(legs: Int) { self.legs = legs }
}

let objects: [AnyObject] = [
    Circle(radius: 2.0),
    Country(area: 243_610),
    Animal(legs: 4)
]

for object in objects {
    // 对迭代出的每一个元素进行检查,看它是否遵循了HasArea协议
    if let objectWithArea = object as? HasArea {
        print("面积为 \(objectWithArea.area)")
    } else {
        print("没有面积")
    }
}

上記のプログラムを実行すると、出力結果は次のようになります:

面积为 12.5663708
面积为 243610.0
没有面积
其他扩展