W końcu zmotywowałem się do aktualizacji mojej odpowiedzi, możesz ponownie rozważyć jej zaakceptowanie.
akashivskyy
@akashivskyy Przyjmuję twoją odpowiedź, ponieważ lepiej prezentuje wszystkie dostępne opcje oraz ich zalety i wady.
Selvin
Odpowiedzi:
503
1. Korzystanie z domyślnych implementacji (preferowane).
protocolMyProtocol{func doSomething()}extensionMyProtocol{func doSomething(){/* return a default value or just leave empty */}}structMyStruct:MyProtocol{/* no compile error */}
Zalety
Nie jest zaangażowane żadne środowisko wykonawcze Objective-C (cóż, przynajmniej nie ma wyraźnego). Oznacza to, że możesz dostosować do niego struktury, wyliczenia i nieklasy NSObject. Oznacza to również, że możesz skorzystać z potężnego systemu generycznego.
Zawsze możesz być pewien, że wszystkie wymagania są spełnione, gdy napotkasz typy zgodne z takim protokołem. Zawsze jest to albo konkretna implementacja, albo domyślna. Tak zachowują się „interfejsy” lub „umowy” w innych językach.
Niedogodności
W przypadku braku Voidwymagań musisz mieć rozsądną wartość domyślną , co nie zawsze jest możliwe. Jednak gdy napotkasz ten problem, oznacza to, że albo taki wymóg nie powinien naprawdę mieć domyślnej implementacji, albo że popełniłeś błąd podczas projektowania interfejsu API.
Nie można odróżnić implementacji domyślnej od żadnej implementacji , przynajmniej bez rozwiązania tego problemu za pomocą specjalnych wartości zwracanych. Rozważ następujący przykład:
Jeśli zapewnisz domyślną implementację, która po prostu zwraca true- na pierwszy rzut oka jest w porządku. Teraz rozważ następujący pseudo kod:
finalclassSomeParser{func parse(data:Data)->[Any]{if/* delegate.validate(value:) is not implemented */{/* parse very fast without validating */}else{/* parse and validate every value */}}}
Nie ma sposobu na wdrożenie takiej optymalizacji - nie możesz wiedzieć, czy Twój delegat wdraża metodę, czy nie.
Chociaż istnieje wiele różnych sposobów rozwiązania tego problemu (przy użyciu opcjonalnych zamknięć, różnych obiektów delegowanych dla różnych operacji, aby wymienić tylko kilka), ten przykład pokazuje problem w jasny sposób.
2. Korzystanie @objc optional.
@objc protocolMyProtocol{@objc optionalfunc doSomething()}classMyClass:NSObject,MyProtocol{/* no compile error */}
Zalety
Domyślna implementacja nie jest wymagana. Po prostu deklarujesz opcjonalną metodę lub zmienną i jesteś gotowy do pracy.
Niedogodności
Ogranicza to znacznie możliwości twojego protokołu, wymagając od wszystkich zgodnych typów zgodności z Objective-C. Oznacza to, że tylko klasy, które dziedziczą, NSObjectmogą być zgodne z takim protokołem. Bez struktur, bez wyliczeń, bez powiązanych typów.
Zawsze musisz sprawdzić, czy opcjonalna metoda jest zaimplementowana , opcjonalnie wywołując ją lub sprawdzając, czy typ zgodny ją implementuje. Może to wprowadzać wiele bojlerów, jeśli często wywołujesz metody opcjonalne.
Spójrz na ulepszony sposób opcjonalnych metod w szybkim użyciu rozszerzenia (poniżej)
Daniel Kanaan,
1
A w jaki sposób testuje się obsługę opcjonalnej metody protokołu w instancji? respondsToSelector?
devios1
3
Po prostu nie rób tego! Swift z jakiegoś powodu nie obsługuje opcjonalnej metody w protokołach.
fpg1503
2
Ta metoda nie obsługuje opcji w parametrach. Tzn. Nie możesz tego zrobićoptional func doSomething(param: Int?)
SoftDesigner,
4
Z pewnością ta odpowiedź jest zasadniczo błędna obecnie, po latach. Dzisiaj w Swift po prostu dodajesz rozszerzenie do domyślnej implementacji, jest to podstawowy aspekt Swift. (Jak pokazują wszystkie współczesne odpowiedzi poniżej). Błędem byłoby obecnie dodać flagę objc.
Fattie
394
W Swift 2 i nowszych można dodawać domyślne implementacje protokołu. To tworzy nowy sposób opcjonalnych metod w protokołach.
protocolMyProtocol{func doSomethingNonOptionalMethod()func doSomethingOptionalMethod()}extensionMyProtocol{func doSomethingOptionalMethod(){// leaving this empty
}}
To nie jest naprawdę fajny sposób na tworzenie opcjonalnych metod protokołów, ale daje możliwość użycia struktur w wywołaniach zwrotnych protokołu.
Jest to prawdopodobnie najczystszy sposób, aby to zrobić w Swift. Szkoda, że nie działa przed Swift 2.0.
Entalpi
13
@MattQuiros Uważam, że w rzeczywistości musisz zadeklarować funkcję w definicji protokołu, w przeciwnym razie funkcja rozszerzenia no-op nie zostanie zastąpiona w twoich klasach zgodnych z protokołem.
Ian Pearce
3
@IanPearce jest poprawny i wydaje się, że jest to zgodne z projektem. W przemówieniu „Programowanie zorientowane na protokół” (408) w WWDC rozmawiają o metodach w głównym protokole, którymi są „punkty dostosowywania” oferowane typom zgodnym. Wymagany punkt dostosowania nie otrzymuje definicji w rozszerzeniu; opcjonalny punkt tak. Metody w protokole, które zasadniczo nie powinny być dostosowywane, są całkowicie deklarowane / definiowane w rozszerzeniu, aby uniemożliwić dostosowywanie typów zgodnych do dostosowywania, chyba że specjalnie wybierzesz opcję DynamicType, aby pokazać, że chcesz niestandardowej implementacji konformatora.
Matthias
4
@FranklinYu Możesz to zrobić, ale następnie zanieczyszczasz swój projekt API za pomocą „rzutów” tam, gdzie w rzeczywistości nie jest potrzebny. Bardziej podoba mi się pomysł „mikro protokołów”. Np. Każda metoda potwierdza jeden protokół, a następnie można sprawdzić: jeśli obiekt jest protokołem
Darko
3
@Antoine Myślę, że powinieneś upublicznić funkcję w rozszerzeniu, ponieważ funkcje w protokołach są z definicji publiczne. Twoje rozwiązanie nie będzie działać, gdy protokół będzie używany poza jego modułem.
Vadim Eisenberg
39
Ponieważ istnieją odpowiedzi na temat używania opcjonalnego modyfikatora i atrybutu @objc do definiowania opcjonalnego protokołu wymagań, dam przykład, jak używać rozszerzeń protokołu definiujących opcjonalny protokół.
Poniższy kod to Swift 3. *.
/// Protocol has empty default implementation of the following methods making them optional to implement:
/// `cancel()`
protocolCancelable{/// default implementation is empty.
func cancel()}extensionCancelable{func cancel(){}}classPlane:Cancelable{//Since cancel() have default implementation, that is optional to class Plane
}let plane =Plane()
plane.cancel()//Print out *UnitedAirlines can't cancelable*
Należy zauważyć, że metody rozszerzenia protokołu nie mogą być wywoływane przez kod Objective-C, a co gorsza, zespół Swift go nie naprawi. https://bugs.swift.org/browse/SR-492
Dobra robota! To powinna być poprawna odpowiedź, ponieważ rozwiązuje problem bez angażowania środowiska wykonawczego Objective-C.
Lukas,
naprawdę fajny!
tania_S,
z szybkiej dokumentacji - „Wymagania protokołu z domyślnymi implementacjami dostarczonymi przez rozszerzenia różnią się od opcjonalnych wymagań protokołu. Chociaż typy zgodne nie muszą przedstawiać własnej implementacji, wymagania z implementacjami domyślnymi można wywoływać bez opcjonalnego łączenia”.
Mec Os
34
Inne odpowiedzi dotyczące oznaczenia protokołu jako „@objc” nie działają, gdy używane są typy szybkie.
structInfo{var height:Intvar weight:Int}@objc protocolHealth{func isInfoHealthy(info:Info)->Bool}//Error"Method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C"
Aby zadeklarować opcjonalne protokoły, które działają dobrze w swift, zadeklaruj funkcje jako zmienne zamiast func.
A następnie zaimplementuj protokół w następujący sposób
classHuman:Health{var isInfoHealthy:(Info)->(Bool)?={ info inif info.weight <200&& info.height >72{returntrue}returnfalse}//Or leave out the implementation and declare it as:
//var isInfoHealthy: (Info) -> (Bool)?
}
Następnie możesz użyć „?” aby sprawdzić, czy funkcja została zaimplementowana
Tylko klasy, protokoły, metody i właściwości mogą używać @objc. Jeśli używasz parametru Enum w definicji metody protokołu @ objc, jesteś skazany.
khunshan
1
@khunshan, ta metoda nie wymaga oznaczania niczego @ objc, o czym mówisz?
Zag
jest to informacja na ten temat, że wyliczenia nie mogą być używane między swift a objc, które dla innych instrukcji można połączyć ze słowem kluczowym @objc.
Myślę, że zanim zapytasz, jak możesz wdrożyć opcjonalną metodę protokołu, powinieneś zapytać, dlaczego powinieneś ją wdrożyć.
Jeśli myślimy o szybkich protokołach jako interfejsie w klasycznym programowaniu obiektowym, opcjonalne metody nie mają większego sensu, a być może lepszym rozwiązaniem byłoby stworzenie domyślnej implementacji lub rozdzielenie protokołu na zestaw protokołów (być może z pewnymi relacjami dziedziczenia między nimi) w celu przedstawienia możliwej kombinacji metod w protokole.
Oto bardzo prosty przykład TYLKO dla szybkich klas, a nie struktur i wyliczeń. Zauważ, że metoda protokołu jest opcjonalna, ma dwa poziomy opcjonalnego łączenia w grę. Również klasa przyjmująca protokół potrzebuje atrybutu @objc w swojej deklaracji.
@objc protocolCollectionOfDataDelegate{optionalfunc indexDidChange(index:Int)}@objc classRootView:CollectionOfDataDelegate{var data =CollectionOfData()init(){
data.delegate =self
data.indexIsNow()}func indexDidChange(index:Int){
println("The index is currently: \(index)")}}classCollectionOfData{var index :Int?weakvar delegate :CollectionOfDataDelegate?func indexIsNow(){
index =23
delegate?.indexDidChange?(index!)}}
Czy możesz opisać nieco bardziej część „ dwa poziomy opcjonalnej gry ”, a mianowicie delegate?.indexDidChange?(index!):?
Unheilig,
2
Gdybyśmy napisali protokół, aby mieć nie opcjonalną metodę taką jak ta: protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) } wtedy wywołalibyśmy go bez znaku zapytania: delegate?.indexDidChange(index!) Kiedy ustawisz opcjonalne wymaganie dla metody w protokole, Typ, który będzie z nią zgodny, NIE może wdrożyć tej metody , więc ?służy do sprawdzania implementacji, a jeśli nie ma, program się nie zawiesi. @Unheilig
Blessing Lopes
weak var delegate : CollectionOfDataDelegate?(zapewnić słabe referencje?)
Alfie Hanssen
@BlessingLopes Czy możesz dodać wyjaśnienie delegate?zastosowania do swojej odpowiedzi? Ta informacja powinna naprawdę należeć do nich w przyszłości. Chcę to zagłosować, ale ta informacja powinna naprawdę znaleźć się w odpowiedzi.
Johnathon Sullinger,
3
jeśli chcesz to zrobić czysto szybko, najlepszym sposobem jest zapewnienie domyślnej implementacji, szczególnie jeśli zwrócisz typ Swift, na przykład struct z typami Swift
Istnieją dwa sposoby utworzenia opcjonalnej metody w szybkim protokole.
1 - Pierwszą opcją jest oznaczenie protokołu za pomocą atrybutu @objc. Chociaż oznacza to, że może być przyjęty tylko przez klasy, oznacza to, że oznaczysz poszczególne metody jako opcjonalne:
2 - Szybszy sposób: Ta opcja jest lepsza. Napisz domyślne implementacje opcjonalnych metod, które nic nie robią, takie jak to.
protocolMyProtocol{func optionalMethod()func notOptionalMethod()}extensionMyProtocol{func optionalMethod(){//this is a empty implementation to allow this method to be optional
}}
Swift ma funkcję o nazwie rozszerzenie, która pozwala nam zapewnić domyślną implementację tych metod, które chcemy być opcjonalne.
Zdefiniuj funkcję w protokole i utwórz rozszerzenie dla tego protokołu, a następnie utwórz pustą implementację dla funkcji, której chcesz używać jako opcjonalnej.
Aby OptionalProtocolszybko zdefiniować , należy użyć @objcsłowa kluczowego przed Protocoldeklaracją i attribute/ methoddeklaracją wewnątrz tego protokołu. Poniżej znajduje się próbka opcjonalnej właściwości protokołu.
@objc protocolProtocol{@objc optionalvar name:String?}classMyClass:Protocol{// No error
}
Chociaż może to odpowiedzieć na pytanie, lepiej dodać opis, w jaki sposób ta odpowiedź może pomóc w rozwiązaniu problemu. Proszę przeczytać Jak napisać dobrą odpowiedź, aby dowiedzieć się więcej.
Roshana Pitigala
-23
Umieść @optionalprzed metodami lub właściwościami.
Odpowiedzi:
1. Korzystanie z domyślnych implementacji (preferowane).
Zalety
Nie jest zaangażowane żadne środowisko wykonawcze Objective-C (cóż, przynajmniej nie ma wyraźnego). Oznacza to, że możesz dostosować do niego struktury, wyliczenia i nieklasy
NSObject
. Oznacza to również, że możesz skorzystać z potężnego systemu generycznego.Zawsze możesz być pewien, że wszystkie wymagania są spełnione, gdy napotkasz typy zgodne z takim protokołem. Zawsze jest to albo konkretna implementacja, albo domyślna. Tak zachowują się „interfejsy” lub „umowy” w innych językach.
Niedogodności
W przypadku braku
Void
wymagań musisz mieć rozsądną wartość domyślną , co nie zawsze jest możliwe. Jednak gdy napotkasz ten problem, oznacza to, że albo taki wymóg nie powinien naprawdę mieć domyślnej implementacji, albo że popełniłeś błąd podczas projektowania interfejsu API.Nie można odróżnić implementacji domyślnej od żadnej implementacji , przynajmniej bez rozwiązania tego problemu za pomocą specjalnych wartości zwracanych. Rozważ następujący przykład:
Jeśli zapewnisz domyślną implementację, która po prostu zwraca
true
- na pierwszy rzut oka jest w porządku. Teraz rozważ następujący pseudo kod:Nie ma sposobu na wdrożenie takiej optymalizacji - nie możesz wiedzieć, czy Twój delegat wdraża metodę, czy nie.
Chociaż istnieje wiele różnych sposobów rozwiązania tego problemu (przy użyciu opcjonalnych zamknięć, różnych obiektów delegowanych dla różnych operacji, aby wymienić tylko kilka), ten przykład pokazuje problem w jasny sposób.
2. Korzystanie
@objc optional
.Zalety
Niedogodności
Ogranicza to znacznie możliwości twojego protokołu, wymagając od wszystkich zgodnych typów zgodności z Objective-C. Oznacza to, że tylko klasy, które dziedziczą,
NSObject
mogą być zgodne z takim protokołem. Bez struktur, bez wyliczeń, bez powiązanych typów.Zawsze musisz sprawdzić, czy opcjonalna metoda jest zaimplementowana , opcjonalnie wywołując ją lub sprawdzając, czy typ zgodny ją implementuje. Może to wprowadzać wiele bojlerów, jeśli często wywołujesz metody opcjonalne.
źródło
respondsToSelector
?optional func doSomething(param: Int?)
W Swift 2 i nowszych można dodawać domyślne implementacje protokołu. To tworzy nowy sposób opcjonalnych metod w protokołach.
To nie jest naprawdę fajny sposób na tworzenie opcjonalnych metod protokołów, ale daje możliwość użycia struktur w wywołaniach zwrotnych protokołu.
Napisałem tutaj małe streszczenie: https://www.avanderlee.com/swift-2-0/optional-protocol-methods/
źródło
Ponieważ istnieją odpowiedzi na temat używania opcjonalnego modyfikatora i atrybutu @objc do definiowania opcjonalnego protokołu wymagań, dam przykład, jak używać rozszerzeń protokołu definiujących opcjonalny protokół.
Poniższy kod to Swift 3. *.
Należy zauważyć, że metody rozszerzenia protokołu nie mogą być wywoływane przez kod Objective-C, a co gorsza, zespół Swift go nie naprawi. https://bugs.swift.org/browse/SR-492
źródło
Inne odpowiedzi dotyczące oznaczenia protokołu jako „@objc” nie działają, gdy używane są typy szybkie.
Aby zadeklarować opcjonalne protokoły, które działają dobrze w swift, zadeklaruj funkcje jako zmienne zamiast func.
A następnie zaimplementuj protokół w następujący sposób
Następnie możesz użyć „?” aby sprawdzić, czy funkcja została zaimplementowana
źródło
Oto konkretny przykład ze schematem delegowania.
Skonfiguruj protokół:
Ustaw delegata na klasę i zaimplementuj protokół. Sprawdź, czy opcjonalna metoda nie musi być zaimplementowana.
Jedną ważną rzeczą jest to, że opcjonalna metoda jest opcjonalna i wymaga „?” podczas dzwonienia. Wspomnij o drugim znaku zapytania.
źródło
W Swift 3.0
Zaoszczędzi to Twój czas.
źródło
@objc
jest teraz potrzebne wszystkim członkom?required
flagą, ale z błędami:required
mogą być używane tylko w deklaracjach „init”.optional
słowo kluczowe przed każdą metodą.źródło
@objc
nie tylko protokołu.Podejście czysto szybkie z dziedziczeniem protokołu:
źródło
Aby zilustrować mechanikę odpowiedzi Antoine'a:
źródło
Myślę, że zanim zapytasz, jak możesz wdrożyć opcjonalną metodę protokołu, powinieneś zapytać, dlaczego powinieneś ją wdrożyć.
Jeśli myślimy o szybkich protokołach jako interfejsie w klasycznym programowaniu obiektowym, opcjonalne metody nie mają większego sensu, a być może lepszym rozwiązaniem byłoby stworzenie domyślnej implementacji lub rozdzielenie protokołu na zestaw protokołów (być może z pewnymi relacjami dziedziczenia między nimi) w celu przedstawienia możliwej kombinacji metod w protokole.
Więcej informacji można znaleźć na stronie https://useyourloaf.com/blog/swift-optional-protocol-methods/ , która daje doskonały przegląd tej kwestii.
źródło
Nieco temat z pierwotnego pytania, ale stanowi rozwinięcie pomysłu Antoine'a i pomyślałem, że może komuś pomóc.
Możesz także ustawić właściwości obliczane jako opcjonalne dla struktur z rozszerzeniami protokołu.
Możesz ustawić właściwość protokołu jako opcjonalną
Zaimplementuj obojętną właściwość obliczoną w rozszerzeniu protokołu
A teraz możesz używać struktur, które mają lub nie mają zaimplementowanej opcjonalnej właściwości
Napisałem również, jak robić opcjonalne właściwości w protokołach Swift na moim blogu , który będę aktualizowany na wypadek, gdyby coś się zmieniło w wydaniach Swift 2.
źródło
Jak utworzyć opcjonalne i wymagane metody delegowania.
źródło
Oto bardzo prosty przykład TYLKO dla szybkich klas, a nie struktur i wyliczeń. Zauważ, że metoda protokołu jest opcjonalna, ma dwa poziomy opcjonalnego łączenia w grę. Również klasa przyjmująca protokół potrzebuje atrybutu @objc w swojej deklaracji.
źródło
delegate?.indexDidChange?(index!)
:?protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) }
wtedy wywołalibyśmy go bez znaku zapytania:delegate?.indexDidChange(index!)
Kiedy ustawisz opcjonalne wymaganie dla metody w protokole, Typ, który będzie z nią zgodny, NIE może wdrożyć tej metody , więc?
służy do sprawdzania implementacji, a jeśli nie ma, program się nie zawiesi. @Unheiligweak var delegate : CollectionOfDataDelegate?
(zapewnić słabe referencje?)delegate?
zastosowania do swojej odpowiedzi? Ta informacja powinna naprawdę należeć do nich w przyszłości. Chcę to zagłosować, ale ta informacja powinna naprawdę znaleźć się w odpowiedzi.jeśli chcesz to zrobić czysto szybko, najlepszym sposobem jest zapewnienie domyślnej implementacji, szczególnie jeśli zwrócisz typ Swift, na przykład struct z typami Swift
przykład:
następnie możesz wdrożyć protokół bez definiowania każdego func
źródło
Istnieją dwa sposoby utworzenia opcjonalnej metody w szybkim protokole.
1 - Pierwszą opcją jest oznaczenie protokołu za pomocą atrybutu @objc. Chociaż oznacza to, że może być przyjęty tylko przez klasy, oznacza to, że oznaczysz poszczególne metody jako opcjonalne:
2 - Szybszy sposób: Ta opcja jest lepsza. Napisz domyślne implementacje opcjonalnych metod, które nic nie robią, takie jak to.
Swift ma funkcję o nazwie rozszerzenie, która pozwala nam zapewnić domyślną implementację tych metod, które chcemy być opcjonalne.
źródło
Jedną z opcji jest przechowywanie ich jako opcjonalnych zmiennych funkcji:
źródło
Zdefiniuj funkcję w protokole i utwórz rozszerzenie dla tego protokołu, a następnie utwórz pustą implementację dla funkcji, której chcesz używać jako opcjonalnej.
źródło
Aby
Optional
Protocol
szybko zdefiniować , należy użyć@objc
słowa kluczowego przedProtocol
deklaracją iattribute
/method
deklaracją wewnątrz tego protokołu. Poniżej znajduje się próbka opcjonalnej właściwości protokołu.źródło
Umieść
@optional
przed metodami lub właściwościami.źródło
@optional
nie jest nawet prawidłowym słowem kluczowym. Jestoptional
i musisz zadeklarować klasę i protokół z tym@objc
atrybutem.