EquatableやSequenceType、その他色々なプロトコルが用意されていますが、実際のコードでどのような使えばいいのかがいまいち思い描けないものもあったので、GitHubに上がっているコードを見て学習しようと思います。比較的よく使いそうなプロトコルに絞って見てみます。
プロトコル名にはApple公式ドキュメントのリンク、実例には該当のコードが載っているページのリンクを貼っているので、詳しく見たい方はリンクをクリックしてください。
Equatable
値が等しいかどうかの比較が可能な型を表すプロトコルです。定義すべき演算子はfunc == (lhs: Self, rhs: Self) -> Bool
です。==
を定義すると自動的に!=
も使えるようになるそうです。
facebook-sdk-swiftの例
extension Photo: Equatable {
public static func == (lhs: Photo, rhs: Photo) -> Bool {
return lhs.sdkPhotoRepresentation == rhs.sdkPhotoRepresentation
}
}
realm-cocoaの例
extension Schema: Equatable {}
public func == (lhs: Schema, rhs: Schema) -> Bool { // swiftlint:disable:this valid_docs
return lhs.rlmSchema.isEqualToSchema(rhs.rlmSchema)
}
Comparable
大小比較が可能な型を表すプロトコルで、Equatableを継承しています。そのためEquatableの演算子==
はもちろん利用できます。演算子<
を定義すると自動的に>
, <=
, >=
も利用できるようになります。func < (lhs: Self, rhs: Self) -> Bool
とfunc == (lhs: Self, rhs: Self) -> Bool
は実装する必要があります。
SwiftyJSONの例
public enum JSONIndex:Comparable
{
case array(Int)
case dictionary(DictionaryIndex<String, JSON>)
case null
static public func ==(lhs: JSONIndex, rhs: JSONIndex) -> Bool
{
switch (lhs, rhs)
{
case (.array(let left), .array(let right)):
return left == right
case (.dictionary(let left), .dictionary(let right)):
return left == right
case (.null, .null): return true
default:
return false
}
}
static public func <(lhs: JSONIndex, rhs: JSONIndex) -> Bool
{
switch (lhs, rhs)
{
case (.array(let left), .array(let right)):
return left < right
case (.dictionary(let left), .dictionary(let right)):
return left < right
default:
return false
}
}
}
SwiftDateの例
public struct DateTimeInterval : Comparable {
public static func ==(lhs: DateTimeInterval, rhs: DateTimeInterval) -> Bool {
return lhs.start == rhs.start && lhs.duration == rhs.duration
}
public static func <(lhs: DateTimeInterval, rhs: DateTimeInterval) -> Bool {
return lhs.compare(rhs) == .orderedAscending
}
}
CustomStringConvertible
selfを表す文字列表現を返す時に使います。NSObjectを継承しているオブジェクトであればdescription関数をprintすると何かしら出力されますが、カスタムクラスだとそれがないので自分でdescription関数を用意する場合はこれを使うことになります。
facebook-sdk-swiftの例
extension AppEventName: CustomStringConvertible {
public var description: String {
return rawValue
}
}
ExpressibleByStringLiteral
ExpressibleByExtendedGraphemeClusterLiteral
を継承し、それがさらにExpressibleByUnicodeScalarLiteral
を継承しています。任意の長さの文字列リテラルを使用して初期化できます。String
, StaticString
はこのプロトコルを継承しています。
realm-cocoaの例
extension SortDescriptor: ExpressibleByStringLiteral {
public typealias UnicodeScalarLiteralType = StringLiteralType
public typealias ExtendedGraphemeClusterLiteralType = StringLiteralType
public init(unicodeScalarLiteral value: UnicodeScalarLiteralType) {
self.init(property: value)
}
public init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType) {
self.init(property: value)
}
public init(stringLiteral value: StringLiteralType) {
self.init(property: value)
}
}
IteratorProtocol
シーケンスの値を1つずつ供給するプロトコルなので、Sequenceプロトコルと密接に関係しています。Sequenceはイテレータを作成することによって要素にアクセスできます。Swiftはシーケンスまたはコレクションのイテレータを内部的に使用することによってfor-inループを可能にしています。
※Swift3で名前がGeneratorType
からIteratorProtocol
になりました。
realm-cocoaの例
public final class RLMIterator: IteratorProtocol {
private let iteratorBase: NSFastEnumerationIterator
internal init(collection: RLMCollection) {
iteratorBase = NSFastEnumerationIterator(collection)
}
public func next() -> RLMObject? {
return iteratorBase.next() as! RLMObject?
}
}
Sequence
for-inで繰り返しが可能な型を表すプロトコルです。
※Swift3で名前がSequenceType
からSequence
になりました。
※繰り返しのためにインスタンス自体を変化させる可能性があるので安全な繰り返しにはCollectionTypeを使った方が良いようです。
Surgeの例
extension Matrix: Sequence {
public func makeIterator() -> AnyIterator<ArraySlice<Element>> {
let endIndex = rows * columns
var nextRowStartIndex = 0
return AnyIterator {
if nextRowStartIndex == endIndex {
return nil
}
let currentRowStartIndex = nextRowStartIndex
nextRowStartIndex += self.columns
return self.grid[currentRowStartIndex..<nextRowStartIndex]
}
}
}
Kingfisherの例
struct BytesSequence: Sequence {
let chunkSize: Int
let data: [UInt8]
func makeIterator() -> BytesIterator {
return BytesIterator(chunkSize: chunkSize, data: data)
}
}
Collection
Sequence、Indexableを継承しています。Sequenceプロトコルから継承するメソッドに加え、特定の位置にある要素にアクセスできるメソッドも使うことができるようになります。
※Swift3で名前がCollectionType
からCollection
になりました。
AudioKitの例
internal struct MIDIDestinations: Collection {
typealias Index = Int
init() { }
var startIndex: Index {
return 0
}
var endIndex: Index {
return MIDIGetNumberOfDestinations()
}
subscript (index: Index) -> MIDIEndpointRef {
return MIDIGetDestination(index)
}
func index(after index: Index) -> Index {
return index + 1
}
}