2010-04-18 11 views
7

Mam trzy klasy, które implementują ten sam protokół i mają tę samą klasę nadrzędną, która nie implementuje protokołu. Normalnie powinienem mieć protokół jako czyste wirtualne funkcje w klasie nadrzędnej, ale nie mogłem znaleźć sposobu, aby to zrobić.Objective-C Wskaźnik do klasy implementującej protokół

Chcę wykorzystać polimorfizm na tych podklasach, deklarując czyste funkcje wirtualne w superklasie, a następnie dzieci będą implementować te funkcje. Jednak jedynym sposobem, jaki udało mi się osiągnąć w tym celu, jest to, aby każde dziecko jednoznacznie zdecydowało się na wdrożenie protokołu. Kiedy robię to w ten sposób, superklasa nie wie, że dzieci będą implementować ten protokół, więc istnieją ostrzeżenia o czasie kompilacji wszędzie wokoło.

Niektóre pseudo-kod, jeśli że nie ma sensu:

@interface superclass: NSObject 
{} 

@interface child1: superclass<MyProtocol> 
{} 

@interface child2: superclass<MyProtocol> 
{} 

Konsument z tych klas:

@class child1 
@class child2 
@class superclass 

@interface SomeViewController: UIViewController 
{ 
    child1 *oneView; 
    child2 *otherView; 
    superclass *currentView; 
} 

-(void) someMethod 
{ 
    [currentView protocolFunction]; 
} 

Jedyny dobry sposób znalazłem zrobić czystych funkcji wirtualnych Objective-C jest hackiem przez umieszczenie [self doesNotRecognizeSelector:_cmd]; w klasie nadrzędnej, ale nie jest idealny, ponieważ spowoduje błędy w czasie wykonywania, a nie czas kompilacji.

+0

Wydaje się, że sposób objc czystych-virtual/metod abstrakcyjnych jest „klasy klastrów”. Może być coś wartego dla ciebie dochodzenia. – nmr

Odpowiedz

5

udało mi się dostać kompilator mnie ostrzec prawidłowo poprzez właściwość superclass *currentView wyglądać następująco:

@property (nonatomic, retain) superclass<MyProtocol> *currentView; 
11

Programiści programu Objective-C często używają sprawdzania dynamicznego, a nie sprawdzania kompilacji w tych sytuacjach, ponieważ język i frameworki tak dobrze je obsługują. Tak na przykład, można napisać metodę tak:

- (void)someMethod 
{ 
    // See if the object in currentView conforms to MyProtocol 
    // 
    if ([currentView conformsToProtocol:@protocol(MyProtocol)]) 
    { 
     // Cast currentView to the protocol, since we checked to make 
     // sure it conforms to it. This keeps the compiler happy. 
     // 
     [(SuperClass<MyProtocol> *) currentView protocolMethod]; 
    } 
} 
+3

Oczywiście bardziej niezawodne jest użycie 'responseSToSelector: @selector (protocolMethod)'. – kennytm

+2

... i konieczne, jeśli protokół ma opcjonalne metody. – jlehr

+0

Nie zachęcam do korzystania z informacji o czasie wykonywania w ten sposób - jest bardzo mało prawdopodobne, aby był to prawdziwy przypadek, w którym nie znasz typu obiektu wystarczająco dobrze w czasie kompilacji, aby tego uniknąć. Najczęściej spotykane przypadki, w których widziałem używany system RTTI, są zbędne z odrobiną refleksji. Wyjątkiem jest implementowanie kodu typu generycznego w Objective-C, w którym, niestety, zmuszeni jesteśmy używać zasadniczo mniej wydajnego mechanizmu, niż kompilator jest w stanie wygenerować kod, po prostu dlatego, że w specyfikacji językowej nie ma generycznych elementów kompilujących (a problem współdzielony z/odziedziczony z C).Również zdalny exec – jheriko

1

Osobiście chciałbym wdrożyć protokół na super klasy, ale wdrożenie metody tak:

- (id) myProtocolMethod { 
    NSAssert(NO, [NSString stringWithFormat:@"-[%@ %@] must be overridden", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]); 
    return nil; 
} 

W ten sposób, jeśli kiedykolwiek zapomnij zastąpić metodę w konkretnej podklasie, powinno to być od razu oczywiste.

+0

Zależy od tego, czy wszystkie podklasy powinny być zgodne z protokołem; nie jestem pewien, co tam ma na myśli Winder. Ale zdecydowanie nie umieszczam tych twierdzeń w moim kodzie produkcyjnym. Wolę po prostu podać puste metody i być może zalogować ostrzeżenie. – jlehr

+0

Właściwie zamierzałem zasugerować coś przeciwnego: rozważmy uczynienie metod protokołów opcjonalnymi i wykonaj test runtime, ale znowu to naprawdę zależy od intencji. – jlehr

+0

To jest zachowanie, które chcę, tylko z błędem kompilatora, a nie z runtime. Wygląda jednak na to, że Objective-C nie ma na to sposobu. – Winder

3

Alternatywnie można użyć następujących

if ([unknownObject conformsToProtocol:@protocol(MyProtocol)]) 
    [unknownObject performSelector:@selector(methodInProtocol)]; 

zamiast następujących jeśli tylko chcą tłumić ostrzeżenie.

if ([unknownObject conformsToProtocol:@protocol(MyProtocol)]) 
    [unknownObject methodInProtocol]; // will cause warning 

performSelector: zadziała tylko wtedy, gdy liczba argumentów wynosi zero lub jeden. Bardziej elastyczne wywołania można osiągnąć za pomocą NSInvocation.

Powiązane problemy