サーチ…


前書き

プロトコルは、オブジェクトの使用方法を指定する方法です。プロトコルはクラス、構造体、または列挙型が提供すべきプロパティとメソッドのセットを記述しますが、プロトコルは実装に制限を与えません。

備考

Swiftプロトコルは、準拠型が実装しなければならない要件の集合です。プロトコルは、配列や一般的な要件など、タイプが予想されるほとんどの場所で使用できます。

プロトコルメンバーは常に、プロトコル全体と同じアクセス修飾子を共有し、別々に指定することはできません。上記の例のように、プロトコルはゲッターまたはセッターの必要条件でアクセスを制限することができますが、

プロトコルの詳細については、「 Swiftプログラミング言語 」を参照してください。

Objective-CプロトコルはSwiftプロトコルに似ています。

プロトコルもJavaインタフェースに匹敵します

プロトコルの基本

プロトコルについて

プロトコルは、プロトコルに準拠したSwiftオブジェクト型(クラス、構造体または列挙型)に必要な初期化子、プロパティ、関数、添字および関連型を指定します。いくつかの言語では、後続のオブジェクトの要件仕様に関する同様の考え方が「インターフェース」として知られています。

宣言され、定義された議定書は、スウィフト機能がパラメタと返品の署名に基づいたタイプである方法と幾分類似している。

スウィフトプロトコル仕様は、プロトコル拡張として知られている機能を介してオプション、明示的に要求、および/または既定の実装が可能です。指定されたすべての要件のために拡張機能を組み込んだプロトコルに従うことを望んでいるSwift Object Type(クラス、構造体または列挙型)は、完全準拠であることの欲求を述べるだけでよい。プロトコル拡張のデフォルトの実装機能は、プロトコルに準拠するすべての義務を果たすのに十分である。

プロトコルは他のプロトコルによって継承することができます。これは、Protocol Extensionsと組み合わせて、プロトコルがSwiftの重要な機能と考えることができることを意味します。

プロトコルとエクステンションは、プログラム設計の柔軟性と開発プロセスに対するSwiftのより広い目的とアプローチを実現するために重要です。 Swiftの議定書と拡張機能の主な目的は、プログラムのアーキテクチャと開発における構成的デザインの促進です。これは、プロトコル指向プログラミングと呼ばれます。 Crustyの古いタイマーは、これをOOPデザインに重点を置くと考えています。

プロトコルは、任意の構造体クラス 、または列挙型で実装できるインタフェースを定義します。

protocol MyProtocol {
    init(value: Int)                      // required initializer
    func doSomething() -> Bool            // instance method
    var message: String { get }           // instance read-only property
    var value: Int { get set }            // read-write instance property
    subscript(index: Int) -> Int { get }  // instance subscript
    static func instructions() -> String  // static method
    static var max: Int { get }           // static read-only property
    static var total: Int { get set }     // read-write static property
}

プロトコルで定義されたプロパティは、 { get }または{ get set }としてアノテートする必要があります。 { get }はプロパティをgettableにする必要があることを意味し、したがって、 任意の種類のプロパティとして実装できます。 { get set }は、プロパティがgettableと同様に設定可能でなければならないことを意味します。

構造体、クラス、または列挙型は、プロトコルに準拠することができます。

struct MyStruct : MyProtocol {
    // Implement the protocol's requirements here
}
class MyClass : MyProtocol {
    // Implement the protocol's requirements here
}
enum MyEnum : MyProtocol {
    case caseA, caseB, caseC
    // Implement the protocol's requirements here
}

プロトコルは、拡張を介して、その要件のいずれかのデフォルト実装を定義することもできます。

extension MyProtocol {
    
    // default implementation of doSomething() -> Bool
    // conforming types will use this implementation if they don't define their own
    func doSomething() -> Bool {
        print("do something!")
        return true
    }
}

associatedtype するタイプの 要件を持たないプロトコルは、タイプとして使用できます

func doStuff(object: MyProtocol) {
    // All of MyProtocol's requirements are available on the object
    print(object.message)
    print(object.doSomething())
}

let items : [MyProtocol] = [MyStruct(), MyClass(), MyEnum.caseA]

また、 複数のプロトコルに準拠する抽象型を定義することもできます。

3.0

Swift 3以上の場合、これはアンパサンド( & )でプロトコルのリストを区切って行います。

func doStuff(object: MyProtocol & AnotherProtocol) {
    // ...
}

let items : [MyProtocol & AnotherProtocol] = [MyStruct(), MyClass(), MyEnum.caseA]
3.0

古いバージョンはシンタックスprotocol<...>持ちますprotocol<...>ここで、プロトコルは山括弧<>囲まれたカンマで区切られたリストです。

protocol AnotherProtocol {
    func doSomethingElse()
}

func doStuff(object: protocol<MyProtocol, AnotherProtocol>) {
    
    // All of MyProtocol & AnotherProtocol's requirements are available on the object
    print(object.message)
    object.doSomethingElse()
}

// MyStruct, MyClass & MyEnum must now conform to both MyProtocol & AnotherProtocol
let items : [protocol<MyProtocol, AnotherProtocol>] = [MyStruct(), MyClass(), MyEnum.caseA]

既存のタイプは、プロトコルに準拠するように拡張することができます。

extension String : MyProtocol {
    // Implement any requirements which String doesn't already satisfy
}

関連するタイプの要件

プロトコルは、 associatedtypeキーワードを使用してassociatedtype するタイプ要件を定義することができます。

protocol Container {
    associatedtype Element
    var count: Int { get }
    subscript(index: Int) -> Element { get set }
}

関連するタイプ要件を持つプロトコルは一般的な制約としてのみ使用できます

// These are NOT allowed, because Container has associated type requirements:
func displayValues(container: Container) { ... }
class MyClass { let container: Container }
// > error: protocol 'Container' can only be used as a generic constraint
// > because it has Self or associated type requirements

// These are allowed:
func displayValues<T: Container>(container: T) { ... }
class MyClass<T: Container> { let container: T }

プロトコルに準拠する型は、 associatedtypeする型が出現することをプロトコルが期待する所与の型を提供することによって、暗黙的にassociatedtype要件を満たすことができる。

struct ContainerOfOne<T>: Container {
    let count = 1          // satisfy the count requirement
    var value: T
    
    // satisfy the subscript associatedtype requirement implicitly,
    // by defining the subscript assignment/return type as T
    // therefore Swift will infer that T == Element
    subscript(index: Int) -> T {
        get {
            precondition(index == 0)
            return value
        }
        set {
            precondition(index == 0)
            value = newValue
        }
    }
}

let container = ContainerOfOne(value: "Hello")

(一般的なプレースホルダの種類の名前は、この例に明確さを追加することに注意してくださいTより適切な名前は次のようになります- Elementのプロトコルの影と思われる、 associatedtype Elementコンパイラは、まだ一般的なプレースホルダのことを推測します。 Element満たすために使用されassociatedtype Element要件。)

associatedtype typealiasは、 typealias使用して明示的に満たすこともできます。

struct ContainerOfOne<T>: Container {

    typealias Element = T
    subscript(index: Int) -> Element { ... }

    // ...
}

同じことが拡張機能にも適用されます:

// Expose an 8-bit integer as a collection of boolean values (one for each bit).
extension UInt8: Container {

    // as noted above, this typealias can be inferred
    typealias Element = Bool

    var count: Int { return 8 }
    subscript(index: Int) -> Bool {
        get {
            precondition(0 <= index && index < 8)
            return self & 1 << UInt8(index) != 0
        }
        set {
            precondition(0 <= index && index < 8)
            if newValue {
                self |= 1 << UInt8(index)
            } else {
                self &= ~(1 << UInt8(index))
            }
        }
    }
}

適合するタイプがすでに要件を満たしている場合、実装は必要ありません。

extension Array: Container {}  // Array satisfies all requirements, including Element

デリゲートパターン

デリゲートは、CocoaおよびCocoaTouchフレームワークで使用される共通のデザインパターンです。このクラスでは、あるクラスがある機能を実装する責任を別のクラスに委譲します。これは、フレームワーククラスがジェネリック機能を実装し、別のデリゲートインスタンスが特定のユースケースを実装するという、懸念の分離の原則に従います。

デリゲートパターンを調べるもう1つの方法は、オブジェクト通信の観点です。 Objectsしばしばお互いに話す必要があり、そうするために、オブジェクトは別のオブジェクトの代理人になるためにprotocolに準拠する必要があります。この設定が完了すると、興味深いものが発生したときにもう一方のオブジェクトがその代理人に返答します。

たとえば、データのリストを表示するユーザーインターフェイスのビューは、どのようなデータを表示するかを決定するのではなく、データの表示方法のロジックのみを担当する必要があります。

もっと具体的な例を見てみましょう。親と子の2つのクラスがある場合:

class Parent { }
class Child { }

そして、子供からの変更を親に通知したい。

スウィフトでは、代表者が使用して実装されているprotocol宣言をし、私たちは宣言しますprotocol delegate実装されますが。ここでdelegateはparentオブジェクトです。

protocol ChildDelegate: class {
    func childDidSomething()
}

子は、デリゲートへの参照を格納するプロパティを宣言する必要があります。

class Child {
    weak var delegate: ChildDelegate?
}

可変気付くdelegateオプションであり、プロトコルChildDelegateこのなし(のみクラス型によって実装されるマークされているdelegate変数として宣言することができないweak任意のサイクルを保持を回避参照。これは場合を意味しないdelegate変数がもはやです他のどこかで参照されると、リリースされます)。これは、親クラスが必要で利用可能なときだけデリゲートを登録するようにするためです。

また、デリゲートをweakマークするために、プロトコル宣言でclassキーワードを追加することによって、ChildDelegateプロトコルを参照型に制約する必要があります。

この例では、子供が何かをして親に通知する必要があるとき、子供は次のように電話します:

delegate?.childDidSomething()

デリゲートが定義されている場合、デリゲートは子供が何かを行ったことを通知されます。

親クラスは、そのアクションに応答できるようにChildDelegateプロトコルを拡張する必要があります。これは親クラスで直接行うことができます:

class Parent: ChildDelegate {
    ...

    func childDidSomething() {
        print("Yay!")
    }
}

または、拡張機能を使用する:

extension Parent: ChildDelegate {
    func childDidSomething() {
        print("Yay!")
    }
}

親はまた、それが子の代表であることを子供に伝える必要があります:

// In the parent
let child = Child()
child.delegate = self

デフォルトでは、Swift protocolはオプションの機能を実装することを許可していません。これらは、プロトコルに@objc属性とoptional修飾子が付けられている場合にのみ指定できます。

たとえば、 UITableViewはiOSのテーブルビューの一般的な動作を実装しますが、ユーザーは、特定のセルの外観と動作を実装するUITableViewDelegateUITableViewDataSourceという2つのデリゲートクラスを実装する必要があります。

@objc public protocol UITableViewDelegate : NSObjectProtocol, UIScrollViewDelegate {
        
        // Display customization
        optional public func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath)
        optional public func tableView(tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int)
        optional public func tableView(tableView: UITableView, willDisplayFooterView view: UIView, forSection section: Int)
        optional public func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath)
        ...
}

クラス定義を変更することによって、このプロトコルを実装することができます。たとえば、次のようにします。

class MyViewController : UIViewController, UITableViewDelegate

プロトコル定義(この場合はUITableViewDelegateoptionalとマークされていないメソッドはすべて実装する必要があります。

準拠している特定のクラスのプロトコル拡張

特定のクラスのデフォルトのプロトコル実装を記述することができます

protocol MyProtocol {
    func doSomething()
}

extension MyProtocol where Self: UIViewController {
    func doSomething() {
        print("UIViewController default protocol implementation")
    }
}

class MyViewController: UIViewController, MyProtocol { }

let vc = MyViewController()
vc.doSomething() // Prints "UIViewController default protocol implementation"

RawRepresentableプロトコル(Extensible Enum)を使用すると、

// RawRepresentable has an associatedType RawValue.
// For this struct, we will make the compiler infer the type
// by implementing the rawValue variable with a type of String
//
// Compiler infers RawValue = String without needing typealias
//
struct NotificationName: RawRepresentable {
    let rawValue: String

    static let dataFinished = NotificationNames(rawValue: "DataFinishedNotification")
}

この構造体は、他の場所に拡張してケースを追加することができます

extension NotificationName {
    static let documentationLaunched = NotificationNames(rawValue: "DocumentationLaunchedNotification")
}

そして、インターフェイスは、任意のRawRepresentable型または具体的にはあなたのenum構造体の周りに設計することができます

func post(notification notification: NotificationName) -> Void {
    // use notification.rawValue
}

コールサイトでは、typesafe NotificationNameにドット構文の省略形を使用できます

post(notification: .dataFinished)

汎用RawRepresentable関数の使用

// RawRepresentable has an associate type, so the 
// method that wants to accept any type conforming to
// RawRepresentable needs to be generic
func observe<T: RawRepresentable>(object: T) -> Void {
    // object.rawValue
}

クラス専用プロトコル

プロトコルは、唯一のように指定することができるクラスを使用してそれを実装することができclassの継承リストにキーワードを。このキーワードは、このリスト内の他の継承されたプロトコルの前に現れなければなりません。

protocol ClassOnlyProtocol: class, SomeOtherProtocol {
    // Protocol requirements 
}

非クラス型がClassOnlyProtocolを実装しようとすると、コンパイラエラーが生成されます。

struct MyStruct: ClassOnlyProtocol { 
    // error: Non-class type 'MyStruct' cannot conform to class protocol 'ClassOnlyProtocol'  
}

他のプロトコルはClassOnlyProtocolから継承することができますが、それらは同じクラスのみの要件を持ちます。

protocol MyProtocol: ClassOnlyProtocol {
    // ClassOnlyProtocol Requirements
    // MyProtocol Requirements
}

class MySecondClass: MyProtocol {
    // ClassOnlyProtocol Requirements
    // MyProtocol Requirements
}

クラスのみのプロトコルの参照セマンティクス

クラスのみのプロトコルを使用すると、準拠型が不明場合に参照セマンティクスが可能になります。

protocol Foo : class {
    var bar : String { get set }
}

func takesAFoo(foo:Foo) {

    // this assignment requires reference semantics,
    // as foo is a let constant in this scope.
    foo.bar = "new value"
}

この例では、 Fooはクラス専用のプロトコルなので、 barへの代入はコンパイラがfooがクラス型であることを知っているので有効であり、したがって参照セマンティクスを持ちます。

Fooがクラス専用のプロトコルでない場合、適合する型は値型である可能性があるため、コンパイラエラーが発生します。これは変更可能にするためにvarアノテーションを必要とするためです。

protocol Foo {
    var bar : String { get set }
}

func takesAFoo(foo:Foo) {
    foo.bar = "new value" // error: Cannot assign to property: 'foo' is a 'let' constant
}

func takesAFoo(foo:Foo) {
    var foo = foo // mutable copy of foo
    foo.bar = "new value" // no error – satisfies both reference and value semantics
}

プロトコルタイプの弱い変数

プロトコル型の変数にweak修飾子を適用する場合、そのプロトコル型はクラスのみでなければなりませんweakは参照型にのみ適用できるからです。

weak var weakReference : ClassOnlyProtocol?

Hashableプロトコルの実装

で使用されるタイプSetsし、 Dictionaries(key)に適合しなければならないHashableから継承プロトコルEquatableプロトコル。

Hashableプロトコルに準拠したカスタムタイプをHashableする必要があります

  • 計算されたプロパティーhashValue
  • 等号演算子ie ==または!=いずれかを定義します。

次の例では、カスタムstruct Hashableプロトコルを実装してHashableます。

struct Cell {
    var row: Int
    var col: Int
    
    init(_ row: Int, _ col: Int) {
        self.row = row
        self.col = col
    }
}

extension Cell: Hashable {
    
    // Satisfy Hashable requirement
    var hashValue: Int {
        get {
            return row.hashValue^col.hashValue
        }
    }

    // Satisfy Equatable requirement
    static func ==(lhs: Cell, rhs: Cell) -> Bool {
        return lhs.col == rhs.col && lhs.row == rhs.row
    }
    
}

// Now we can make Cell as key of dictonary
var dict = [Cell : String]()

dict[Cell(0, 0)] = "0, 0"
dict[Cell(1, 0)] = "1, 0"
dict[Cell(0, 1)] = "0, 1"

// Also we can create Set of Cells
var set = Set<Cell>()

set.insert(Cell(0, 0))
set.insert(Cell(1, 0))

:カスタムタイプの異なる値が異なるハッシュ値を持つ必要はなく、衝突は許容されます。ハッシュ値が等しい場合、等価演算子を使用して実際の等価性が決定されます。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow