Swift 5: Menerapkan operasi array umum pada struct yang mengimplementasikan Equatable with protocol

Oct 08 2020

Saya memiliki 2 struct sederhana di aplikasi saya yang sesuai dengan protokol, untuk mengaktifkan beberapa operasi umum yang perlu saya terapkan pada kedua jenis:

import UIKit

protocol Track {
    var url: URL { get set }
}

struct TrackFromMediaFile: Track {
    var url: URL
}

struct TrackFromAsset: Track {
    var url: URL
}

Sekarang saya memiliki daftar struct ini:

var tracks = [Track]()
tracks.append(TrackFromMediaFile(url: URL(string: "http://www.remotefile")!))
tracks.append(TrackFromAsset(url: URL(string: "local://localfile")!))

Saya perlu menemukan indeks trek tertentu dalam daftar trek ini:

// throws: 
// Value of protocol type 'Track' cannot conform to 'Equatable'; only struct/enum/class types can conform to protocols
var track = tracks[0]
tracks.firstIndex(of: track)

Saya mencoba mengikuti pendekatan dari https://stackoverflow.com/a/46719045/319905 dan https://developer.apple.com/videos/play/wwdc2015/408/ sia-sia, saya tidak dapat menyesuaikan ini dengan contoh saya.

Bagaimana saya bisa mengimplementasikan penanganan array trek dengan cara yang umum?

Jawaban

2 RobNapier Oct 08 2020 at 07:18

Tidak begitu jelas kesetaraan seperti apa yang Anda cari. Jika Anda ingin memeriksa persamaan jenis dan persamaan nilai, Anda dapat melakukannya dengan cara ini:

tracks.firstIndex(where: {
    type(of: $0) == type(of: track) && $0.url == track.url
})

Jika Anda hanya ingin memvalidasi itu urlsama, Anda akan melakukannya dengan cara ini:

tracks.firstIndex(where: { $0.url == track.url })

Di Swift, Equatable berarti suatu nilai dapat dibandingkan dengan nilai lain yang berjenis sama. Ini tidak berlaku untuk nilai dari tipe yang berbeda. Sebuah protokol bukanlah sebuah tipe, ia menjelaskan tipe, jadi Track itu sendiri tidak dapat disamakan.

Jika Anda akan sering melakukan ini, sering kali lebih mudah untuk menyatakan "dapat memeriksa kesetaraan dengan anggota protokol lainnya". Itu tidak bisa disamakan, tetapi itu pasti sesuatu yang bisa Anda ungkapkan. Ini terlihat seperti ini:

protocol Track {
    var url: URL { get set }
    func isEqual(to: Track) -> Bool
}

Untuk jenis Track-conforming yang sudah Equatable, Anda dapat memberikan implementasi default:

extension Track where Self: Equatable {
    func isEqual(to other: Track) -> Bool {
        guard let other = other as? Self else { return false }
        return self == other
    }
}

Dan tandai tipe Anda yang sesuai sebagai Equatable (swift secara otomatis akan membuat kesesuaian):

struct TrackFromMediaFile: Track, Equatable { ... }
struct TrackFromAsset: Track, Equatable { ... }

Dengan itu, kodenya sedikit lebih bagus:

tracks.firstIndex(where: { $0.isEqual(to: track) })

Dan jika Anda sering melakukannya, tentu saja Anda bisa membuatnya sedikit lebih bagus:

extension Collection where Element == Track {
    func firstIndex(of track: Track) {
        firstIndex(where: { $0.isEqual(to: track) })
    }
}

Dan dengan itu, Anda dapat memiliki sintaks yang Anda mulai:

tracks.firstIndex(of: track)