Definiowanie kategorii protokołów w Objective-C?

W Objective-C Mogę dodać metody do istniejących klas z kategorią, np.

@interface NSString (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end

Czy można to zrobić również z protokołami, tzn. jeśli istniał protokół NSString, coś w stylu:

@interface <NSString> (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end

Chcę to zrobić, ponieważ mam kilka rozszerzeń do NSObject (klasy), używając tylko publicznych metod NSObject i chcę, aby te rozszerzenia działały również z obiektami implementującymi protokół .

Aby podać kolejny przykład, co jeśli chcę napisać metodę logDescription, który wypisuje opis obiektu do dziennika:

- (void) logDescription {
    NSLog(@"%@", [self description]);
}

Mogę oczywiście dodać tę metodę do NSObject, ale są inne klasy, które nie dziedziczą po NSObject, gdzie również chciałbym mieć tę metodę, np. NSProxy. Ponieważ metoda używa tylko publicznych członków protokołu, najlepiej byłoby dodać go do protokołu.

Edit: Java 8 ma teraz to z "virtual extension methods" w interfejsach: http://cr.openjdk.java.net/ ~ briangoetz / lambda / Defender%20methods%20v4. pdf . to jest dokładnie to, co chciałbym zrobić w Objective-C. nie widziałem, aby to pytanie zarabiało tyle uwagi...

Pozdrawiam, Jochen

Author: Jochen, 2009-10-05

7 answers

ExtObjC ma najbardziej schludne rzeczy , które możesz zrobić z protokołami / kategoriami... po pierwsze to @concreteprotocol...

  • definiuje "konkretny protokół", który może dostarczać domyślne implementacje metod wewnątrz protokołu.
  • blok @protocol powinien istnieć w pliku nagłówkowym, a odpowiadający mu blok @concreteprotocol w pliku implementacji.
  • każdy obiekt, który deklaruje się jako zgodny z tym protokołem, otrzyma implementację swojej metody, ale tylko wtedy, gdy żadna metoda przez ta sama nazwa już istnieje.

MyProtocol.h

@protocol MyProtocol 
@required - (void)someRequiredMethod;
@optional - (void)someOptionalMethod;
@concrete - (BOOL)isConcrete;   

MyProtocol.m

 @concreteprotocol(MyProtocol) - (BOOL)isConcrete { return YES; } ...

Więc zadeklarowanie obiektu MyDumbObject : NSObject <MyProtocol> automatycznie zwróci YES do isConcrete.

Mają również pcategoryinterface(PROTOCOL,CATEGORY), który "definiuje interfejs dla kategorii o nazwie CATEGORY on a protocol PROTOCOL". Kategorie protokołów zawierają metody, które są automatycznie stosowane do każdej klasy, która deklaruje, że jest zgodna z protokołem."Istnieje również towarzyszące makro do wykorzystania w pliku implementacji. Zobacz dokumenty.

Ostatni, ale nie najmniej / Nie bezpośrednio związany z @protocols jest synthesizeAssociation(CLASS, PROPERTY), który " syntetyzuje właściwość dla klasy przy użyciu powiązanych obiektów. Jest to przede wszystkim przydatne do dodawania właściwości do klasy w ramach kategorii. Właściwość musi być zadeklarowana za pomocą @property w interfejsie podanej klasy (lub kategorii na niej) i musi być typu object."

Tak wiele narzędzi w tej bibliotece otwartych (way-up) rzeczy, które można zrobić z ObjC... od wielokrotnego dziedziczenia... cóż, Twoja wyobraźnia jest granicą.

 17
Author: Alex Gray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-02 13:43:58

Krótka odpowiedź: Nie.

Długa odpowiedź: JAK TO BĘDZIE DZIAŁAĆ? Wyobraź sobie, że możesz Dodać metody do istniejących protokołów? Jak to działa? Wyobraź sobie, że chcieliśmy dodać inną metodę do NSCoding, powiedzmy -(NSArray *) codingKeys; ta metoda jest wymaganą metodą, która zwraca tablicę kluczy używanych do kodowania obiektu.

Problem polega na tym, że istnieją istniejące klasy (jak np. NSString), które już implementują NSCoding, ale nie implementują naszej metody codingKeys. Co powinno się stać? Jak czy wstępnie skompilowany framework wie, co zrobić, gdy ta wymagana wiadomość zostanie wysłana do klasy, która jej nie implementuje?

Można powiedzieć "możemy dodać definicję tej metody poprzez kategorię" lub "możemy powiedzieć, że wszelkie metody dodane przez te kategorie protokołu są jawnie opcjonalne". Tak, możesz to zrobić i teoretycznie obejść problem, który opisałem powyżej. Ale jeśli masz zamiar to zrobić, równie dobrze możesz po prostu zrobić z tego kategorię na pierwszym miejscu, a następnie sprawdź, czy klasa respondsToSelector: przed wywołaniem metody.

 25
Author: Dave DeLong,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-05 17:45:54

Chociaż prawdą jest, że nie możesz zdefiniować kategorii dla protokołów (i nie chcesz, ponieważ nie wiesz nic o istniejącym obiekcie), możesz zdefiniować kategorie w taki sposób, że kod odnosi się tylko do obiektu danego typu, który ma pożądany protokół (coś w rodzaju częściowej specjalizacji szablonów C++).

Głównym zastosowaniem czegoś takiego jest zdefiniowanie kategorii, która zależy od dostosowanej wersji klasy. (Wyobraź sobie, że mam Podklasy UIViewController, które są zgodne z protokołem Foo, co oznacza, że mają właściwość foo, mój kod kategorii może potrzebować właściwości foo, ale nie mogę zastosować jej do protokołu Foo, i jeśli po prostu zastosuję ją do UIViewController, kod nie będzie kompilowany domyślnie, a zmuszanie go do kompilacji oznacza, że ktoś robi introspekcję lub po prostu spieprza, może wywołać Twój kod, który zależy od protokołu. Podejście hybrydowe może działać tak:

@protocol Foo
- (void)fooMethod

@property (retain) NSString *foo;
@end

@implementation UIViewController (FooCategory)

- (void)fooMethod {
    if (![self conformsToProtocol:@protocol(Foo)]) {
        return;
    }

    UIViewController<Foo> *me = (UIViewController<Foo>*) self;
    // For the rest of the method, use "me" instead of "self"
    NSLog(@"My foo property is \"%@\"", me.foo);
}
@end

Z podejściem hybrydowym, ty może napisać kod tylko raz (na klasę, która ma zaimplementować protokół) i mieć pewność, że nie wpłynie to na instancje klasy, które nie są zgodne z protokołem.

Minusem jest to, że synteza/definicja właściwości nadal musi mieć miejsce w poszczególnych podklasach.

 22
Author: Douglas Mayle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-14 14:25:41

To nie ma sensu, ponieważ protokół nie może zaimplementować tej metody. Protokół jest sposobem deklarowania, że wspierasz niektóre metody. Dodanie metody do tej listy poza protokołem oznacza, że wszystkie "zgodne" klasy przypadkowo deklarują nową metodę, mimo że jej nie implementują. Jeśli jakaś Klasa zaimplementowała protokół NSObject, ale nie zszedł z NSObject, a następnie dodałeś metodę do protokołu, która złamałaby klasę zgodność.

Można jednak utworzyć nowy protokół, który zawiera stary z deklaracją typu @protocol SpecialObject <NSObject>.

 7
Author: Chuck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-05 17:33:27

Myślę, że możesz mieszać terminy tu i tam. Rozszerzenia, kategorie, protokoły, Interfejsy i klasy to różne rzeczy w Objective-C. W Język Objective-C 2.0 Apple bardzo dobrze opisuje różnice, w tym zalety i wady korzystania z kategorii i rozszerzeń.

Jeśli się nad tym zastanowić, czym jest "kategoria" lub "rozszerzenie" w sensie pojęciowym? To sposób na dodanie funkcjonalności do klasy. W Objective-C protokoły są zaprojektowany tak, aby nie miał żadnej implementacji. Jak zatem dodać lub rozszerzyć implementację czegoś, co nie ma implementacji?

 0
Author: slf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-05 17:34:45

Jeśli już piszesz kategorię, dlaczego po prostu nie dodać definicji Protokołu w nagłówku zaraz po definicji kategorii?

Tzn.

@interface NSString (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
@end

@protocol MyExtendedProtocolName <NSString>
//Method declarations go here
@end

W ten sposób każda klasa importująca nagłówek kategorii również otrzyma definicję protokołu i będzie mogła dodać ją do swojej klasy..

@interface MyClass <OriginalProtocol,MyExtendedProtocolName>

Uważaj również podczas podkategoryzowania NSString, jest to klaster i nie zawsze możesz uzyskać zachowanie, którego oczekujesz.

 0
Author: pxl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-05 18:49:53

Adam Sharp opublikował rozwiązanie, które zadziałało.

Składa się z 3 kroków:

  1. Definiowanie metod, które chcesz dodać jako @optional w protokole.
  2. uczynienie obiektów, które chcesz rozszerzyć, zgodnymi z tym protokołem.
  3. kopiowanie tych metod do tych obiektów w czasie wykonywania.

Sprawdź link, aby uzyskać pełne informacje.

 0
Author: Senseful,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:00:35