W Swift mogę jawnie ustawić typ zmiennej, deklarując ją w następujący sposób:
var object: TYPE_NAME
Jeśli chcemy pójść o krok dalej i zadeklarować zmienną, która jest zgodna z wieloma protokołami, możemy użyć protocol
deklaratywnego:
var object: protocol<ProtocolOne,ProtocolTwo>//etc
A co jeśli chciałbym zadeklarować obiekt, który jest zgodny z jednym lub kilkoma protokołami, a także ma określony typ klasy bazowej? Odpowiednik Objective-C wyglądałby następująco:
NSSomething<ABCProtocolOne,ABCProtocolTwo> * object = ...;
W Swift spodziewałbym się, że będzie to wyglądać tak:
var object: TYPE_NAME,ProtocolOne//etc
Daje nam to elastyczność, ponieważ jesteśmy w stanie poradzić sobie z implementacją typu podstawowego, a także dodanym interfejsem zdefiniowanym w protokole.
Czy jest inny bardziej oczywisty sposób, którego mógłbym przegapić?
Przykład
Jako przykład załóżmy, że mam UITableViewCell
fabrykę, która jest odpowiedzialna za zwrot komórek zgodnych z protokołem. Możemy łatwo ustawić ogólną funkcję, która zwraca komórki zgodne z protokołem:
class CellFactory {
class func createCellForItem<T: UITableViewCell where T:MyProtocol >(item: SpecialItem,tableView: UITableView) -> T {
//etc
}
}
później chcę usunąć z kolejki te komórki, wykorzystując zarówno typ, jak i protokół
var cell: MyProtocol = CellFactory.createCellForItem(somethingAtIndexPath) as UITableViewCell
Zwraca to błąd, ponieważ komórka widoku tabeli nie jest zgodna z protokołem ...
Chciałbym móc określić, że komórka jest a UITableViewCell
i jest zgodna z MyProtocol
deklaracją w zmiennej?
Usprawiedliwienie
Jeśli znasz wzorzec fabryki, miałoby to sens w kontekście możliwości zwracania obiektów określonej klasy, które implementują określony interfejs.
Tak jak w moim przykładzie, czasami lubimy definiować interfejsy, które mają sens, gdy są stosowane do konkretnego obiektu. Mój przykład komórki widoku tabeli jest jednym z takich uzasadnień.
Chociaż dostarczony typ nie jest dokładnie zgodny ze wspomnianym interfejsem, obiekt zwracany przez fabrykę ma, dlatego chciałbym mieć elastyczność w interakcji zarówno z typem klasy bazowej, jak iz deklarowanym interfejsem protokołu
źródło
NSSomething<ABCProtocolOne,ABCProtocolTwo> * object = ...;
. Przedmiot ten wydaje się zupełnie bezużyteczny, ponieważNSSomething
już wie, do czego się dostosowuje. Jeśli nie jest zgodny z jednym z protokołów<>
, wystąpiąunrecognised selector ...
awarie. Nie zapewnia to żadnego bezpieczeństwa typu.Odpowiedzi:
W Swift 4 można teraz zadeklarować zmienną, która jest podklasą typu i implementuje jeden lub więcej protokołów w tym samym czasie.
Aby wykonać opcjonalną zmienną:
lub jako parametr metody:
Apple ogłosił to na WWDC 2017 w sesji 402: Co nowego w Swift
źródło
Nie możesz zadeklarować zmiennej, takiej jak
ani nie deklaruj typu zwracanego funkcji, jak
Możesz zadeklarować jako parametr funkcji, taki jak ten, ale jest to w zasadzie rzutowanie w górę.
Na razie wszystko, co możesz zrobić, to:
Dzięki temu technicznie
cell
jest identyczny zasProtocol
.Ale, co do kompilatora,
cell
maUITableViewCell
tylko interfejs , podczas gdyasProtocol
ma tylko interfejs protokołów. Tak więc, gdy chcesz wywołaćUITableViewCell
metody, musisz użyćcell
zmiennej. Jeśli chcesz wywołać metodę protokołów, użyjasProtocol
zmiennej.Jeśli masz pewność, że komórka jest zgodna z protokołami, nie musisz ich używać
if let ... as? ... {}
. lubić:źródło
-> UITableViewCell<MyProtocol>
, to jest nieprawidłowe, ponieważUITableViewCell
nie jest typem ogólnym. Myślę, że to nawet się nie kompiluje.cell
ma tylko metody protokołów (dla kompilatora).Niestety Swift nie obsługuje zgodności z protokołem na poziomie obiektu. Istnieje jednak nieco niezręczna metoda obejścia, która może służyć Twoim celom.
Następnie, gdziekolwiek potrzebujesz zrobić cokolwiek, co ma UIViewController, uzyskasz dostęp do aspektu .viewController struktury i wszystkiego, czego potrzebujesz, aspektu protokołu, odniesiesz się do .protocol.
Na przykład:
Teraz za każdym razem, gdy potrzebujesz mySpecialViewController do zrobienia czegokolwiek związanego z UIViewController, wystarczy odwołać się do mySpecialViewController.viewController, a gdy potrzebujesz go do wykonania jakiejś funkcji protokołu, odwołaj się do mySpecialViewController.protocol.
Miejmy nadzieję, że Swift 4 pozwoli nam w przyszłości zadeklarować obiekt z dołączonymi do niego protokołami. Ale na razie to działa.
Mam nadzieję że to pomoże!
źródło
Może się mylę, ale czy nie mówisz o dodaniu zgodności protokołu do
UITableCellView
klasy? W tym przypadku protokół jest rozszerzany na klasę bazową, a nie obiekt. Zapoznaj się z dokumentacją Apple dotyczącą deklarowania przyjęcia protokołu z rozszerzeniem, które w Twoim przypadku wyglądałoby mniej więcej tak:Oprócz wspomnianej już dokumentacji języka Swift, zobacz także artykuł Nate Cooks Funkcje ogólne dla niezgodnych typów z dalszymi przykładami.
Protokół Adoption zrobi to, sprawi, że obiekt będzie zgodny z podanym protokołem. Należy jednak pamiętać o niekorzystnej stronie, że zmienna danego typu protokołu nie wie nic poza protokołem. Ale można to obejść, definiując protokół, który ma wszystkie potrzebne metody / zmienne / ...
Jeśli chcesz, aby metoda ogólna, zmienna była zgodna zarówno z protokołem, jak i typami klas bazowych, możesz mieć pecha. Ale wygląda na to, że musisz zdefiniować protokół wystarczająco szeroki, aby mieć wymagane metody zgodności, a jednocześnie wystarczająco wąski, aby mieć możliwość zaadaptowania go do klas bazowych bez zbytniego nakładu pracy (tj. Po prostu deklarując, że klasa jest zgodna z protokół).
źródło
Kiedyś miałem podobną sytuację, próbując połączyć moje ogólne połączenia między czynnikami w Storyboardach (IB nie pozwoli ci łączyć gniazd z protokołami, tylko instancjami obiektów), którą obejrzałem, po prostu maskując publiczną ivar klasy bazowej za pomocą prywatnego obliczonego własność. Chociaż nie uniemożliwia to komuś dokonywania nielegalnych przydziałów jako takich, zapewnia wygodny sposób bezpiecznego zapobiegania wszelkim niepożądanym interakcjom z niezgodnym wystąpieniem w czasie wykonywania. (tj. zapobiegaj wywoływaniu metod delegatów do obiektów, które nie są zgodne z protokołem).
Przykład:
„OutputReceiver” jest deklarowany jako opcjonalny, podobnie jak prywatny „protocolOutputReceiver”. Uzyskując zawsze dostęp do outputReceiver (czyli delegata) za pośrednictwem tego ostatniego (właściwość computed), skutecznie odfiltrowuję wszelkie obiekty, które nie są zgodne z protokołem. Teraz mogę po prostu użyć opcjonalnego łączenia łańcuchowego, aby bezpiecznie wywołać obiekt delegata, niezależnie od tego, czy implementuje protokół, czy nawet istnieje.
Aby zastosować to do swojej sytuacji, możesz ustawić publiczny ivar typu „YourBaseClass?” (w przeciwieństwie do AnyObject) i użyj prywatnej właściwości obliczonej, aby wymusić zgodność protokołu. FWIW.
źródło