プロトコルにオプショナル要件を定義することができます。この要件を、プロトコルに準拠する型で実装する必要はありません。オプショナル要件では、プロトコルの定義の一部として optional を前に付けます。オプショナル要件でメソッドやプロパティを使用するとき、自動的にその型がオプショナルになります。例えば、(Int) -> String 型のメソッドは ((Int) -> String)? になります。メソッドの戻り値だけでなく、関数型全体がオプショナルでラップされることに注目してください。

プロトコルに準拠する型で要件が実装されない可能性があるため、オプショナルプロトコル要件をオプショナルチェーンで呼び出すことができます。オプショナルメソッドを呼び出すとき、メソッドの名前の後に someOptionalMethod?(someArgument) のようにクエスチョンマークを記述して、オプショナルメソッドの実装をチェックします。オプショナルチェーンの情報は、Optional Chaining を確認してください。

NOTE

プロトコルに @objc 属性が付けられている場合、オプショナルプロトコル要件のみ指定することができます。

この属性は、プロトコルが Objective-C に露出していることを示し、『Using Swift with Cocoa and Objective-C (Swift 2.2)』で説明されています。Objectvie-C と相互運用しない場合であっても、オプショナル要件を指定したい場合には、プロトコルに @objc 属性を付ける必要があります。

また、Objective-C クラスや、他の @objc クラスを継承するクラスのみ、プロトコルを採用することができることに注意してください。構造体や列挙型では採用できません。

以降の例では、整数をカウントするクラス Counter を定義し、増加量を提供する外部のデータソースを使用します。このデータソースは CounterDataSource プロトコルで定義され、オプショナル要件が 2 つあります。

@objc protocol CounterDataSource {
    optional func incrementForCount(count: Int) -> Int
    optional var fixedIncrement: Int { get }
}

CounterDataSource プロトコルは、オプショナルメソッド要件 incrementForCount(_:) とオプショナルプロパティ要件 fixedIncrement を定義しています。これらの要件は、Counter インスタンスに適切な増加量を提供するデータソースの 2 つの異なる手段を定義しています。

NOTE
厳密に言えば、どちらのプロトコル要件も実装することなく、CounterDataSource に準拠するクラスを記述することができます。それらの要件はともにオプショナルです。技術的には許容されますが、データソースとして役に立ちません。

次の Counter クラスは、CounterDataSource? 型のオプショナルな dataSource プロパティを定義しています。

class Counter {
    var count = 0
    var dataSource: CounterDataSource?
    func increment() {
        if let amount = dataSource?.incrementForCount?(count) {
            count += amount
        } else if let amount = dataSource?.fixedIncrement {
            count += amount
        }
    }
}

Counter クラスは変数プロパティ count に現在値を保持します。またh、Counter クラスはメソッドが呼び出される度に count プロパティを増加するメソッド increment を定義しています。

increment() メソッドは、はじめにデータソースの incrementForCount(_:) メソッドの実装を探して増加量を取り出そうとします。increment() メソッドは、incrementForCount(_:) を呼び出すためにオプショナルチェーンを使用し、メソッドの引数として count の現在値を渡します。

ここでは、オプショナルチェーンが 2 階層になっています。1 つ目は、dataSource が nil である可能性があるため、dataSource が nil でない場合にのみ incrementForCount(_:) が呼び出されることを示すために、dataSource の名前の後にクエスチョンマークを付けます。2 つ目は、detaSource が存在している場合で、incrementForCount(_:) がオプショナル要件であるため、実装されている保証がありません。ここでは、incrementForCount(_:) が実装されていない可能性があり、これもまたオプショナルチェーンで扱われています。incrementForCount(_:) が存在している、つまり nil でない場合にのみ、incrementForCount(_:) の呼び出しが起こります。このため、incrementForCount(_:) もまた名前の後にクエスチョンマークを記述しています。

incrementForCall(_:) の呼び出しがこれら 2 つのいずれかの理由で失敗する可能性があるため、呼び出しはオプショナルな Int 値を返します。これは、CounterDataSource の定義でオプショナルでない Int 値を返すように incrementForCount(_:) が定義されている場合でもそうなります。オプショナルチェーンの処理が続けて 2 つありますが、結果は 1 つのオプショナルでラップされます。複数のオプショナルチェーンの処理についての詳細な情報は、Linking Multiple Levels of Chaining を確認してください。

incrementForCount(_:) を呼び出した後、返されるオプショナル Int は、オプショナルバインディングで定数 amount にアンラップされます。オプショナル Int に値がある場合、つまりデリゲートとメソッドの両方が存在し、メソッドが値を返した場合、アンラップされた amount はストアドプロパティの count に追加され、増加処理が完了します。

dataSource が nil なため、あるいはデータソースが incrementForCount(_:) を実装していないために incrementForCount(_:) メソッドから値を取り出すことができない場合、increment() メソッドは代わりにデータソースの fixedIncrement プロパティから値を取り出そうとします。fixedIncrement プロパティもまたオプショナル要件なため、CounterDataSource プロトコル定義の一部として fixedIncrement がオプショナルでない Int プロパティとして定義されているとしても、その値はオプショナルな Int 値になります。

次は、常にデータソースが定数値 3 を返すシンプルな CounterDataSource の実装です。fixedIncrement オプショナルプロパティ要件を実装しています。

class ThreeSource: NSObject, CounterDataSource {
    let fixedIncrement = 3
}

新しい Counter インスタンスのデータソースとして、ThreeSource のインスタンスを使用することができます。

var counter = Counter()
counter.dataSource = ThreeSource()
for _ in 1...4 {
    counter.increment()
    print(counter.count)
}
// 3
// 6
// 9
// 12

このコードは新しい Counter インスタンスを生成し、新しい ThreeSource インスタンスがそのデータソースになるように設定し、そしてカウンターの increment() メソッドを 4 回呼び出しています。期待どおりに、increment() が呼び出されるたびに、カウンターの count プロパティが 3 増加しています。

次はより複雑なデータソース TowardsZeroSource で、Counter インスタンスが現在の count 値からゼロに向けて増加または減少させます。

@objc class TowardsZeroSource: NSObject, CounterDataSource {
    func incrementForCount(count: Int) -> Int {
        if count == 0 {
            return 0
        } else if count < 0 {
            return 1
        } else {
            return -1
        }
    }
}

TowardsZeroSource クラスは、CounterDataSource プロトコルからオプショナルメソッド incrementForCount(_:) を実装し、どちらの方向に計算するかを判定するために count 引数値を使用しています。count がすでにゼロの場合、さらに計算が行われないようにメソッドは 0 を返します。

-4 からゼロまでカウントするために、既存の Counter インスタンスに TowardsZeroSource インスタンスを使用することができます。カウンターがゼロに達すると、それ以上は計算されません。

counter.count = -4
counter.dataSource = TowardsZeroSource()
for _ in 1...5 {
    counter.increment()
    print(counter.count)
}
// -3
// -2
// -1
// 0
// 0

Portions of this page are translations based on work created and shared by Apple and used according to terms described in the Creative Commons Attribution 4.0 International License.