Методът на делегиране изглежда @required, когато е деклариран като @optional

Създадох свой собствен делегат за клас ObjC. Самият клас се занимава с операции с основни данни. Делегираните методи се използват за информиране на други класове за промени, настъпили в хранилището за данни. Класът, който се занимава с хранилището за данни, се нарича Datastore, а неговият делегат се нарича DatastoreDelegate. Моят UIViewController (ContactsViewController) имплементира делегата.

Моят DatastoreDelegate е деклариран, както следва:

@class Datastore;
@protocol DatastoreDelegate <NSObject>;
@optional
- (void)didAddMessage:(Message *)message;
- (void)didUpdateContact:(Contact *)contact;
- (void)didAddContact:(Contact *)contact;
- (void)didUpdateContact:(Contact *)contact;
- (void)didDeleteContacts;
@end

Странното е, че кодът ми работи добре с тези методи, с изключение на метода [didAddMessage:]. Всеки път, когато се опитам да извикам този делегат от класа Datastore, получавам грешка от ContactsViewController. Грешката ми казва, че селекторът [didAddMessage:] липсва в екземпляра на ContactsViewController (неразпознат селектор, изпратен до екземпляра). Как може селекторът да липсва, ако не е задължителен?

Трябва да отбележа, че моят клас Datastore е Singleton. Не съм сигурен дали това е свързано по някакъв начин с проблема, който имам.


person Wolfgang Schreurs    schedule 30.01.2010    source източник


Отговори (2)


„По избор“ означава, че повикващият е отговорен за проверката дали целта отговаря на даден селектор. напр.:

if ([_myDelegate respondsToSelector:@selector(didAddMessage:)])
{
    [_myDelegate didAddMessage:theMessage];
}
person Darren    schedule 30.01.2010

Внедрихте ли didAddMessage: във вашия ContactsViewController? Не е задължително, така че не сте принудени да го внедрите, но ако изпратите съобщението didAddMessage: до ContactsViewController, но всъщност не сте го приложили в ContactsViewController, пак ще получите предупреждение от компилатора. С други думи, @optional просто означава, че не трябва да го прилагате, но компилаторът все още може да даде предупреждение, ако не сте го внедрили, но се опитате да го използвате.

Това, което може да искате да направите в Datastore е следното:

if ([delegate respondsToSelector:@selector(didAddMessage:)]) {
    [delegate didAddMessage:theMessage];
}

а не просто:

[delegate didAddMessage:theMessage];

(Все пак ще получите предупреждение на компилатора в първия пример, но е безопасно да го игнорирате, тъй като проверявате по време на изпълнение, за да видите дали подходящият метод е внедрен в делегата.)

person mipadi    schedule 30.01.2010
comment
Благодаря, съобщението [respondsToSelector:] реши проблема ми. Някак странно, че се изисква такова съобщение, когато самите съобщения са декларирани като @optional. - person Wolfgang Schreurs; 30.01.2010
comment
Помислете за това: ако методът не е задължителен, тогава класът делегат може да не го е имплементирал. Ако изпратите съобщение, на което делегатът не отговори, тогава ще получите срив (по подразбиране). - person mipadi; 30.01.2010
comment
@optional просто означава, че клас, който казва, че изпълнява протокол, не е задължен да прилага този метод. Нещото, което ви обърква — обаждащият се проверява [respondsToSelector:] — е начинът, по който работят всички делегирани съобщения. - person Jon Reid; 30.01.2010
comment
Този отговор ми харесва повече, защото отговарям по-подробно - person Heavy_Bullets; 18.10.2013
comment
Намирам за разочароващо, че Objective C прави всичко възможно, за да направи правилното нещо, ако извикате метод на указател NULL, но когато извикате метод, за който изрично сте казали, че е незадължителен, той се срива! - person Andrew; 06.02.2014