Czy jest to możliwe w Swift? Jeśli nie, to czy istnieje obejście tego problemu?
Czy jest to możliwe w Swift? Jeśli nie, to czy istnieje obejście tego problemu?
Odpowiedzi:
protocol MyProtocol {
func doSomething()
}
extension MyProtocol {
func doSomething() {
/* return a default value or just leave empty */
}
}
struct MyStruct: 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 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:
protocol SomeParserDelegate {
func validate(value: Any) -> Bool
}
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:
final class SomeParser {
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.
@objc optional
.@objc protocol MyProtocol {
@objc optional func doSomething()
}
class MyClass: NSObject, MyProtocol {
/* no compile error */
}
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.
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.
protocol MyProtocol {
func doSomethingNonOptionalMethod()
func doSomethingOptionalMethod()
}
extension MyProtocol {
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.
Napisałem tutaj małe streszczenie: https://www.avanderlee.com/swift-2-0/optional-protocol-methods/
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()`
protocol Cancelable {
/// default implementation is empty.
func cancel()
}
extension Cancelable {
func cancel() {}
}
class Plane: Cancelable {
//Since cancel() have default implementation, that is optional to class Plane
}
let plane = Plane()
plane.cancel()
// Print out *United Airlines 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
Inne odpowiedzi dotyczące oznaczenia protokołu jako „@objc” nie działają, gdy używane są typy szybkie.
struct Info {
var height: Int
var weight: Int
}
@objc protocol Health {
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.
protocol Health {
var isInfoHealthy: (Info) -> (Bool)? { get set }
}
A następnie zaimplementuj protokół w następujący sposób
class Human: Health {
var isInfoHealthy: (Info) -> (Bool)? = { info in
if info.weight < 200 && info.height > 72 {
return true
}
return false
}
//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
func returnEntity() -> Health {
return Human()
}
var anEntity: Health = returnEntity()
var isHealthy = anEntity.isInfoHealthy(Info(height: 75, weight: 150))?
//"isHealthy" is true
Oto konkretny przykład ze schematem delegowania.
Skonfiguruj protokół:
@objc protocol MyProtocol:class
{
func requiredMethod()
optional func optionalMethod()
}
class MyClass: NSObject
{
weak var delegate:MyProtocol?
func callDelegate()
{
delegate?.requiredMethod()
delegate?.optionalMethod?()
}
}
Ustaw delegata na klasę i zaimplementuj protokół. Sprawdź, czy opcjonalna metoda nie musi być zaimplementowana.
class AnotherClass: NSObject, MyProtocol
{
init()
{
super.init()
let myInstance = MyClass()
myInstance.delegate = self
}
func requiredMethod()
{
}
}
Jedną ważną rzeczą jest to, że opcjonalna metoda jest opcjonalna i wymaga „?” podczas dzwonienia. Wspomnij o drugim znaku zapytania.
delegate?.optionalMethod?()
W Swift 3.0
@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
Zaoszczędzi to Twój czas.
@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ą.@objc
nie tylko protokołu.
Aby zilustrować mechanikę odpowiedzi Antoine'a:
protocol SomeProtocol {
func aMethod()
}
extension SomeProtocol {
func aMethod() {
print("extensionImplementation")
}
}
class protocolImplementingObject: SomeProtocol {
}
class protocolImplementingMethodOverridingObject: SomeProtocol {
func aMethod() {
print("classImplementation")
}
}
let noOverride = protocolImplementingObject()
let override = protocolImplementingMethodOverridingObject()
noOverride.aMethod() //prints "extensionImplementation"
override.aMethod() //prints "classImplementation"
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.
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ą
protocol SomeProtocol {
var required: String { get }
var optional: String? { get }
}
Zaimplementuj obojętną właściwość obliczoną w rozszerzeniu protokołu
extension SomeProtocol {
var optional: String? { return nil }
}
A teraz możesz używać struktur, które mają lub nie mają zaimplementowanej opcjonalnej właściwości
struct ConformsWithoutOptional {
let required: String
}
struct ConformsWithOptional {
let required: String
let optional: String?
}
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.
Jak utworzyć opcjonalne i wymagane metody delegowania.
@objc protocol InterViewDelegate:class {
@objc optional func optfunc() // This is optional
func requiredfunc()// This is required
}
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 protocol CollectionOfDataDelegate{
optional func indexDidChange(index: Int)
}
@objc class RootView: CollectionOfDataDelegate{
var data = CollectionOfData()
init(){
data.delegate = self
data.indexIsNow()
}
func indexDidChange(index: Int) {
println("The index is currently: \(index)")
}
}
class CollectionOfData{
var index : Int?
weak var delegate : CollectionOfDataDelegate?
func indexIsNow(){
index = 23
delegate?.indexDidChange?(index!)
}
}
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. @Unheilig
weak 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:
struct magicDatas {
var damagePoints : Int?
var manaPoints : Int?
}
protocol magicCastDelegate {
func castFire() -> magicDatas
func castIce() -> magicDatas
}
extension magicCastDelegate {
func castFire() -> magicDatas {
return magicDatas()
}
func castIce() -> magicDatas {
return magicDatas()
}
}
następnie możesz wdrożyć protokół bez definiowania każdego func
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:
@objc protocol MyProtocol {
@objc optional func optionalMethod()
}
2 - Szybszy sposób: Ta opcja jest lepsza. Napisz domyślne implementacje opcjonalnych metod, które nic nie robią, takie jak to.
protocol MyProtocol {
func optionalMethod()
func notOptionalMethod()
}
extension MyProtocol {
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.
Jedną z opcji jest przechowywanie ich jako opcjonalnych zmiennych funkcji:
struct MyAwesomeStruct {
var myWonderfulFunction : Optional<(Int) -> Int> = nil
}
let squareCalculator =
MyAwesomeStruct(myWonderfulFunction: { input in return input * input })
let thisShouldBeFour = squareCalculator.myWonderfulFunction!(2)
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 Optional
Protocol
szybko zdefiniować , należy użyć @objc
słowa kluczowego przed Protocol
deklaracją i attribute
/ method
deklaracją wewnątrz tego protokołu. Poniżej znajduje się próbka opcjonalnej właściwości protokołu.
@objc protocol Protocol {
@objc optional var name:String?
}
class MyClass: Protocol {
// No error
}
Umieść @optional
przed metodami lub właściwościami.
@optional
nie jest nawet prawidłowym słowem kluczowym. Jest optional
i musisz zadeklarować klasę i protokół z tym @objc
atrybutem.