Rzutuj wystąpienie klasy na @protocol w Objective-C

102

Mam obiekt (UIViewController), który może, ale nie musi, być zgodny z protokołem, który zdefiniowałem.

Wiem, że potrafię określić, czy obiekt jest zgodny z protokołem, a następnie bezpiecznie wywołaj metodę:

if([self.myViewController conformsToProtocol:@protocol(MyProtocol)]) {
    [self.myViewController protocolMethod]; // <-- warning here
}

Jednak XCode wyświetla ostrzeżenie:

warning 'UIViewController' may not respond to '-protocolMethod'

Jaki jest właściwy sposób uniknięcia tego ostrzeżenia? Wydaje się, że nie mogę obsadzić self.myViewControllerw MyProtocolklasie.

Bród
źródło

Odpowiedzi:

172

Prawidłowy sposób na zrobienie tego to:

if ([self.myViewController conformsToProtocol:@protocol(MyProtocol)])
{
        UIViewController <MyProtocol> *vc = (UIViewController <MyProtocol> *) self.myViewController;
        [vc protocolMethod];
}

UIViewController <MyProtocol> *Typu cast przekłada się na „VC jest obiektem UIViewController, które odpowiada MyProtocol”, natomiast przy użyciu id <MyProtocol>przekłada się na „VC jest przedmiotem nieznanej klasy, które odpowiada MyProtocol”.

W ten sposób kompilator da ci odpowiednie sprawdzenie typu vc- kompilator da ci ostrzeżenie tylko wtedy, gdy jakakolwiek metoda, która nie jest zadeklarowana na żadnym z nich UIViewControllerlub <MyProtocol>zostanie wywołana. idpowinno być używane tylko w sytuacji, gdy nie znasz klasy / typu rzutowanego obiektu.

Nick Forge
źródło
2
Korzystając z protokołów, naprawdę nie powinieneś przejmować się typem obiektu - celem protokołu jest to, że każdy typ obiektu może go przyjąć i być używany bez konieczności rzutowania na określony obiekt. Dlatego zalecałbym użycie odpowiedzi @andy wszędzie tam, gdzie przesyłasz do protokołu, zamiast powyższego - id<MyProtocol> p = (id<MyProtocol>)self.myViewController;ta odpowiedź i @andys są poprawne, ale jego jest bardziej poprawne.
memmons
2
@Answerbot Twój komentarz jest niepoprawny i pomija kwestię, którą przedstawiłem w ostatnim akapicie mojej odpowiedzi. Typ obiektu może Cię interesować lub nie, zależy to od sytuacji. Co się dzieje, jeśli chcesz wysłać wiadomość zadeklarował UIViewControllersię vcna przykład w mojej odpowiedzi, i to zadeklarowane jako id <MyProtocol>?
Nick Forge
Nie wiesz, co w odniesieniu do mojego komentarza jest nieprawidłowe? W każdym razie, jeśli sprawdzasz, czy obiekt jest zgodny z protokołem, dlaczego miałbyś wtedy wywołać inną metodę niezwiązaną z protokołem? Nie przypominam sobie, żeby kiedykolwiek musiałem to zrobić lub zobaczyć to w kodzie, który sprawdziłem. Wydaje mi się, że to zapach kodu.
memmons
To, że go nie widziałeś / nie używałeś, nie oznacza, że ​​jest to zapach kodu. Oto fragment kodu pokazujący jeden przykład sytuacji, w których wyrzucanie informacji o typie przez użycie idjest problemem: gist.github.com/nsforge/7743616
Nick Forge,
60

Możesz to rzucić w ten sposób:

if([self.myViewController conformsToProtocol:@protocol(MyProtocol)])
{
    id<MyProtocol> p = (id<MyProtocol>)self.myViewController;
    [p protocolMethod];
}

To też trochę mnie zrzuciło. W Objective-C protokół nie jest samym typem, więc musisz określić id(lub inny typ, taki jak NSObject) wraz z protokołem, który chcesz.

Andy
źródło
Ach, spoko, dzięki. Właśnie sprawdziłem i zobaczyłem, że odlewanie go również (id)działa. Czy to zła forma?
Ford
1
Jeśli rzucisz go jako identyfikator <MyProtocol>, kompilator ostrzeże Cię, jeśli użyjesz metod, które nie są zdefiniowane w tym protokole.
dreamlax
1
@dreamlax - w ten sposób kompilator sprawdza typy względem protokołów. Więcej informacji można znaleźć na stronie developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/… .
Andy
1
@Ford - lepiej byłoby użyć protokołu specjalnie, ponieważ w ten sposób kompilator może wykonać za Ciebie pewne sprawdzenie typu.
Andy
1
@Andy, myślę, że nie potrzebujesz znaku „*”, ponieważ „id” jest już wskaźnikiem. A więc: id <MyProtocol> p = (id <MyProtocol>) self.myViewController; [p protokółMethod]; Lub po prostu: [(id <MyProtocol>) self.myViewController protocolMethod];
Ford,