Używanie protokołów jako typów tablic i parametrów funkcji w swift


Chcę utworzyć klasę, która może przechowywać obiekty zgodne z określonym protokołem. Obiekty muszą być przechowywane w tablicy określonego typu. Zgodnie z dokumentacją Swift protokoły mogą być używane jako typy:

Ponieważ jest to typ, możesz używać protokołu w wielu miejscach, w których dozwolone są inne typy, w tym:
  • Jako parametr lub typ zwracany w funkcji, metodzie lub inicjatorze
  • Jako typ stałej, zmiennej lub właściwości
  • Jako typ elementów w tablicy, słowniku lub innym kontenerze
Jednak następujące prowadzi do błędów kompilatora:

Protokół „SomeProtocol” może być używany tylko jako ogólne ograniczenie, ponieważ ma własne lub powiązane wymagania dotyczące typu

Jak zamierzasz to rozwiązać:
protocol SomeProtocol: Equatable {
func bla()
}class SomeClass {

var protocols = [SomeProtocol]()

func addElement(element: SomeProtocol) {
self.protocols.append(element)
}

func removeElement(element: SomeProtocol) {
if let index = find(self.protocols, element) {
self.protocols.removeAtIndex(index)
}
}
}

Zaproszony:
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Masz do czynienia z wariantem problemu z protokołem w Swift, dla którego nie istnieje jeszcze dobre rozwiązanie.
Zobacz też

rozszerzanie tablicy, aby sprawdzić, czy jest posortowana w języku Swift?
https://coderoad.ru/24602595/, zawiera sugestie, jak obejść ten problem, co może być odpowiednie dla konkretnego problemu (Twoje pytanie jest bardzo ogólne, być może możesz znaleźć obejście, korzystając z tych odpowiedzi).
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Chcesz utworzyć klasę ogólną z ograniczeniem typu, który wymaga, aby klasy były używane z nią zgodne z
SomeProtocol
, na przykład:
class SomeClass<T: SomeProtocol> {
typealias ElementType = T
var protocols = [ElementType]() func addElement(element: ElementType) {
self.protocols.append(element)
} func removeElement(element: ElementType) {
if let index = find(self.protocols, element) {
self.protocols.removeAtIndex(index)
}
}
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Swift ma specjalną klasę protokołów, która nie zapewnia polimorfizmu w typach, które go implementują. Takie protokoły używają w swoich definicjach słów kluczowych
Self
lub
relatedtype
(a
Equatable
jest jednym z nich).
W niektórych przypadkach możesz użyć wymazywalnego opakowania, aby nadać swojej kolekcji homomorfizm. Poniżej przykład.
// This protocol doesn't provide polymorphism over the types which implement it.
protocol X: Equatable {
var x: Int { get }
}// We can't use such protocols as types, only as generic-constraints.
func ==<T: X>(a: T, b: T) -> Bool {
return a.x == b.x
}// A type-erased wrapper can help overcome this limitation in some cases.
struct AnyX {
private let _x: () -> Int
var x: Int { return _x() } init<T: X>(_ some: T) {
_x = { some.x }
}
}// Usage Examplestruct XY: X {
var x: Int
var y: Int
}struct XZ: X {
var x: Int
var z: Int
}let xy = XY(x: 1, y: 2)
let xz = XZ(x: 3, z: 4)//let xs = [xy, xz]// error
let xs = [AnyX(xy), AnyX(xz)]
xs.forEach { print($0.x) }// 1 3
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Ograniczone rozwiązanie, które znalazłem, polega na oznaczeniu protokołu jako protokołu tylko dla klasy. Umożliwi to porównywanie obiektów za pomocą operatora „===”.
Zdaję sobie sprawę, że to nie zadziała dla struktur i tak dalej, ale w moim przypadku było wystarczająco dobre.
protocol SomeProtocol: class {
func bla()
}class SomeClass { var protocols = [SomeProtocol]() func addElement(element: SomeProtocol) {
self.protocols.append(element)
} func removeElement(element: SomeProtocol) {
for i in 0...protocols.count {
if protocols[i] === element {
protocols.removeAtIndex(i)
return
}
}
}}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Rozwiązanie jest dość proste:
protocol SomeProtocol {
func bla()
}class SomeClass {
init() {} var protocols = [SomeProtocol]() func addElement<T: SomeProtocol where T: Equatable>(element: T) {
protocols.append(element)
} func removeElement<T: SomeProtocol where T: Equatable>(element: T) {
protocols = protocols.filter {
if let e = $0 as? T where e == element {
return false
}
return true
}
}
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Jak rozumiem, Twoim głównym celem jest utrzymanie kolekcji obiektów zgodnych z jakimś protokołem, dodawanie i usuwanie z tej kolekcji. Jest to funkcja określona w kliencie „SomeClass”. Równe dziedziczenie wymaga siebie, a to nie jest potrzebne do tej funkcji. Moglibyśmy sprawić, by działało to na tablicach w Obj-C przy użyciu funkcji „index”, która może akceptować niestandardowy komparator, ale nie jest to obsługiwane w języku Swift. Najprostszym rozwiązaniem jest więc użycie słownika zamiast tablicy, jak pokazano w kodzie poniżej. Udostępniłem metodę getElements (), która zwróci żądaną tablicę protokołów. W ten sposób każdy, kto używa SomeClass, nie będzie nawet wiedział, że do implementacji jest używany słownik.
Ponieważ i tak będziesz potrzebować jakiejś charakterystycznej właściwości, aby oddzielić swoje obiekty, założyłem, że to „nazwa”. Upewnij się, że do element.name = "foo" podczas tworzenia nowej instancji SomeProtocol. Jeśli nie podano nazwy, nadal możesz utworzyć instancję, ale nie zostanie ona dodana do kolekcji, a metoda addElement () zwróci wartość „false”.
protocol SomeProtocol {
var name:String? {get set}// Since elements need to distinguished,
//we will assume it is by name in this example.
func bla()
}class SomeClass {//var protocols = [SomeProtocol]()//find is not supported in 2.0, indexOf if
// There is an Obj-C function index, that find element using custom comparator such as the one below, not available in Swift
/*
static func compareProtocols(one:SomeProtocol, toTheOther:SomeProtocol)->Bool {
if (one.name == nil) {return false}
if(toTheOther.name == nil) {return false}
if(one.name == toTheOther.name!) {return true}
return false
}
*///The best choice here is to use dictionary
var protocols = [String:SomeProtocol]()
func addElement(element: SomeProtocol) -> Bool {
//self.protocols.append(element)
if let index = element.name {
protocols[index] = element
return true
}
return false
} func removeElement(element: SomeProtocol) {
//if let index = find(self.protocols, element) {// find not suported in Swift 2.0
if let index = element.name {
protocols.removeValueForKey(index)
}
} func getElements() -> [SomeProtocol] {
return Array(protocols.values)
}
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

znalazłem

nie

czyste-czyste rozwiązanie Swift na tym blogu:

http://blog.inferis.org/blog/2 ... ocols
http://blog.inferis.org/blog/2 ... cols/
/
Sztuczka polega na dopasowaniu
NSObjectProtocol
, ponieważ wprowadza on
isEqual ()
.
Dlatego zamiast korzystać z protokołu
Equatable
i jego standardowego zastosowania
==
, możesz napisać własną funkcję, aby znaleźć element i go usunąć.
Oto implementacja Twojej funkcji
find (tablica, element) - & > Int?
:
protocol SomeProtocol: NSObjectProtocol {}func find(protocols: [SomeProtocol], element: SomeProtocol) -> Int? {
for (index, object) in protocols.enumerated() {
if (object.isEqual(element)) {
return index
}
} return nil
}

Uwaga: w tym przypadku, twoje obiekty pasujące do
SomeProtocol
muszą dziedziczyć po
NSObject
.

Aby odpowiedzieć na pytania, Zaloguj się lub Zarejestruj się