Swift Language
プロトコル
サーチ…
前書き
プロトコルは、オブジェクトの使用方法を指定する方法です。プロトコルはクラス、構造体、または列挙型が提供すべきプロパティとメソッドのセットを記述しますが、プロトコルは実装に制限を与えません。
備考
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]
また、 複数のプロトコルに準拠する抽象型を定義することもできます。
Swift 3以上の場合、これはアンパサンド( &
)でプロトコルのリストを区切って行います。
func doStuff(object: MyProtocol & AnotherProtocol) {
// ...
}
let items : [MyProtocol & AnotherProtocol] = [MyStruct(), MyClass(), MyEnum.caseA]
古いバージョンはシンタックス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のテーブルビューの一般的な動作を実装しますが、ユーザーは、特定のセルの外観と動作を実装するUITableViewDelegate
とUITableViewDataSource
という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
プロトコル定義(この場合はUITableViewDelegate
でoptional
とマークされていないメソッドはすべて実装する必要があります。
準拠している特定のクラスのプロトコル拡張
特定のクラスのデフォルトのプロトコル実装を記述することができます 。
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))
注 :カスタムタイプの異なる値が異なるハッシュ値を持つ必要はなく、衝突は許容されます。ハッシュ値が等しい場合、等価演算子を使用して実際の等価性が決定されます。