Swiftのタイプ消去
8167 ワード
まずは本文のDemoをお届けします
Swiftのタイプ消去を聞いたことがあるかどうかはわかりませんが、タイプ消去とは、ある実例の実際のタイプを暴露せず、外部に必要なタイプだけを暴露することです.例えば、私たちが
私たちがフィボラッチの反復器を実現した後、私たちは外部にこの反復器を使ってフィボラッチシーケンスを生成させたいと思っていますが、私は私が
タイプ消去という小さなテクニックを知っている以上、車輪の作り方を学びましょう.まず、標準ライブラリを真似て消去タイプの反復器を実装します.まず、上記の手順に従ってプロトコルを定義し、2つの反復器を実装します.
次に、反復器包装の箱を実現し、対外的に反復器
ここで2つの問題が発生しました.1つ目の問題は、私たちが定義した汎用
まず、
このような方法は実現が非常に簡単で便利であり,標準ライブラリでも同様の効果を実現している.ただし、
まず、さっきお話ししたニーズを明確にしなければなりません.我々が実装した
まず,
最後に、
ここで、私たちの
方法1については、
これにより,Swift標準ライブラリにおける
標準ライブラリのタイプ消去
Swiftのタイプ消去を聞いたことがあるかどうかはわかりませんが、タイプ消去とは、ある実例の実際のタイプを暴露せず、外部に必要なタイプだけを暴露することです.例えば、私たちが
class
またはstruct
を作成してプロトコルを実現したとき、私たちがこの例を対外的に提供したとき、これがプロトコルを実現したことを外部に知らせたいだけですが、このプロトコルを実現したclass
またはstruct
がどのタイプなのかを外部に知られたくありません.このとき、私たちはタイプ消去を使用する必要があります.実際には、Swiftの標準ライブラリにはこのようなものがあります.struct FibsIterator: IteratorProtocol {
var status = (0, 1)
mutating func next() -> Int? {
guard status.0 < 100 else { return nil }
let num = status.0
self.status = (status.1, status.0 + status.1)
return num
}
}
struct SquareIterator: IteratorProtocol {
var status = 1
mutating func next() -> Int? {
guard status < 100 else { return nil }
defer {
status = status * 2
}
return status
}
}
// fibsIterator type: AnyIterator
let fibsIterator = AnyIterator(FibsIterator())
// squareIterator type: AnyIterator
let squareIterator = AnyIterator(SquareIterator())
// anyIteratorArray type: [AnyIterator]
let anyIteratorArray = [fibsIterator, squareIterator]
// fibsSequence type: AnySequence
let fibsSequence = AnySequence { return FibsIterator() }
// Collection
// c type: AnyCollection
let c = AnyCollection(Array())
IteratorProtocol
、Sequence
、Collection
についてはここでは紹介しませんが…私たちがフィボラッチの反復器を実現した後、私たちは外部にこの反復器を使ってフィボラッチシーケンスを生成させたいと思っていますが、私は私が
FibsIterator
このstruct
で実現したことを外部に知られたくありません.そこで私たちはAnyIterator
、AnySequence
、AnyCollection
で包装し、外部の使用者はこれを反復するときにInt
タイプの反復器、シーケンス、または集合を生成することしか知らない.これにより、FibsIterator
のタイプを非表示にします.車輪を作ってみる
タイプ消去という小さなテクニックを知っている以上、車輪の作り方を学びましょう.まず、標準ライブラリを真似て消去タイプの反復器を実装します.まず、上記の手順に従ってプロトコルを定義し、2つの反復器を実装します.
protocol MyIteratorProtocol {
associatedtype Element
mutating func next() -> Self.Element?
}
struct FibsIterator: MyIteratorProtocol {
var status = (0, 1)
mutating func next() -> Int? {
guard status.0 < 100 else { return nil }
let num = status.0
self.status = (status.1, status.0 + status.1)
return num
}
}
struct SquareIterator: MyIteratorProtocol {
var status = 1
mutating func next() -> Int? {
guard status < 100 else { return nil }
defer {
status = status * 2
}
return status
}
}
次に、反復器包装の箱を実現し、対外的に反復器
next()
方法で戻されたタイプ(ここではIntタイプを例に挙げる)だけを暴露し、実装された反復器タイプを暴露しないで、最初はこのようなコードを書く可能性があります.class IteratorBox {
var iterator: I
init(_ iterator: I) {
self.iterator = iterator
}
func next() -> I.Element? {
return iterator.next()
}
}
// fibsIteratorBox type: IteratorBox
let fibsIteratorBox = IteratorBox(FibsIterator())
// squareIteratorBox type: IteratorBox
let squareIteratorBox = IteratorBox(SquareIterator())
// ! , Boxs [Any]
let Boxs = [fibsIteratorBox, squareIteratorBox]❌
ここで2つの問題が発生しました.1つ目の問題は、私たちが定義した汎用
I
が直接暴露されたことです.これは私たちの初心に反して、私たちが外部に見たいのはI.Element
のタイプで、I
を暴露するタイプではありません.2つ目の問題は,これらIteratorBox
を配列で格納すると,汎用型が異なるためエラーが報告されることである.方法1:属性保存反復器メソッドの実装
まず、
MyIteratorProtocol
を実装するタイプが露出することを防止するために、IteratorBox
の汎用型はMyIteratorProtocol
のElement
タイプであり、MyIteratorProtocol
のタイプではないことを明らかにした.次に、IteratorBox
実装MyIteratorProtocol
プロトコルも必要です.IteratorBox
に完全な機能が含まれていることを確認します.struct IteratorBox: MyIteratorProtocol {
private var nextIMP: () -> A?
init(_ iterator: I) where I.Element == A {
var iteratorCopy=iterator//Swiftのパラメータは にletと されます
self.nextIMP = { iteratorCopy.next() }
}
func next() -> A? {
return nextIMP()
}
}
//fibsIteratorBoxのtype:IteratorBox
let fibsIteratorBox = IteratorBox(FibsIterator())
//squareIteratorBoxのtype:IteratorBox
let squareIteratorBox = IteratorBox(SquareIterator())
//Boxのtype:[IteratorBox]
let Boxs = [fibsIteratorBox, squareIteratorBox]
このような方法は実現が非常に簡単で便利であり,標準ライブラリでも同様の効果を実現している.ただし、
MyIteratorProtocol
は、複数のメソッドごとに1つの属性を追加して保存し、init
メソッドでも追加のコードを記述する必要があるため、メソッドの少ないタイプにのみ適用されます.MyIteratorProtocol
メソッドの数が多い場合、MyIteratorProtocol
を実装する反復器を上記のように保存し、反復器によって呼び出すしかありません.しかし、上の車輪のところは試したのではないでしょうか.しかも問題が多いですね...次の方法を見てみましょう方法2:オブジェクト向けの継承とマルチステート特性を利用する
まず、さっきお話ししたニーズを明確にしなければなりません.我々が実装した
IteratorBox
の汎用型はMyIteratorProtocol
のElement
タイプであるべきであり、実装MyIteratorProtocol
の反復器を保存するために、汎用型を実装MyIteratorProtocol
のタイプに制約する必要があり、これにより衝突をもたらす.1つのクラスがこの2つのタイプを同時に満たすことができない以上、私たちは2つのクラスで実現します.class IteratorBox: MyIteratorProtocol {
func next() -> A? {
fatalError("This method is abstract, you need to implement it!")
}
}
まず,
IteratorBox
実装MyIteratorProtocol
のすべての方法を実装し,方法の実装ではそれをクラッシュさせるだけである.次に、IteratorBoxHelper
を実装して反復器を保存し、プロトコル内の方法を実装します.class IteratorBoxHelper {
var iterator: I
init(_ iterator: I) {
self.iterator = iterator
}
func next() -> I.Element? {
return iterator.next()
}
}
最後に、
IteratorBoxHelper
をIteratorBox
から継承します.class IteratorBoxHelper: IteratorBox {
var iterator: I
init(_ iterator: I) {
self.iterator = iterator
}
override func next() -> I.Element? {
return iterator.next()
}
}
IteratorBox
を継承する場合、汎用A
のタイプをIteratorBoxHelper
のI
のElement
のタイプに制約します.反復器をIteratorBoxHelper
でパッケージした後、このタイプをIteratorBox
と宣言し、いわゆるアップシフト(サブクラスタイプを親クラスタイプと宣言)について、これらの反復器を呼び出し、メソッドを呼び出すと、多態性の特徴に基づいて実際のタイプのメソッドが呼び出されます.// fibsIteratorBox type: IteratorBox
let fibsIteratorBox: IteratorBox = IteratorBoxHelper(FibsIterator())
// squareIteratorBox type: IteratorBox
let squareIteratorBox: IteratorBox = IteratorBoxHelper(SquareIterator())
// Boxs type: [IteratorBox]
let Boxs = [fibsIteratorBox, squareIteratorBox]
print(" fibsIteratorBox next ")
while let num = fibsIteratorBox.next() {
print(num)
}
print(" squareIteratorBox next ")
while let num = squareIteratorBox.next() {
print(num)
}
パッケージ後に値の意味がなくなりました
ここで、私たちの
FibsIterator
とSquareIterator
はすべてstruct
で、値の意味を持っていますが、別の変数に値を割り当てた後、反復して書き込みを行うとコピーされます.var fib1 = FibsIterator()
print(fib1.next()!) // 0
print(fib1.next()!) // 1
print(fib1.next()!) // 1
var fib2 = fib1
print(fib1.next()!) // 2
print(fib1.next()!) // 3
print(fib2.next()!) // 2
print(fib2.next()!) // 3
方法1については、
IteratorBox
のタイプは、標準ライブラリのAnyIterator
と同じstruct
であるが、両方とも書き込み時にコピーする特性を備えていない場合、IteratorBox
の閉パケット属性はiteratorCopy
を取得し、別の変数に値を割り当てても、閉パケット属性は1つのiteratorCopy
を参照する.方法2では、IteratorBox
とIteratorBoxHelper
が両方class
タイプであるため、参照の意味となり、別の変数に値を割り当てるときはポインタコピーのみとなる.let fibsIteratorBox: IteratorBox = IteratorBoxHelper(FibsIterator())
print(fibsIteratorBox.next()!) // 0
print(fibsIteratorBox.next()!) // 1
print(fibsIteratorBox.next()!) // 1
let fibsBox2 = fibsIteratorBox
print(fibsIteratorBox.next()!) // 2
print(fibsIteratorBox.next()!) // 3
print(fibsBox2.next()!) // 5
print(fibsBox2.next()!) // 8
これにより,Swift標準ライブラリにおける
AnyIterator
の実装を模倣することに成功した.間違いがあれば、よろしくお願いします.