Diversamente dal concetto di ereditarietà, che in Objective-C è singola e quindi impedisce a una classe di estendere più di una classe, non esistono vincoli relativi al numero di protocolli a cui una classe può essere conforme.
Immaginiamo un protocollo DriverelessVehicleProtocol, e facendo riferimento alla classe Car della lezione precedente, vediamo come renderla conforme anche a questo nuovo protocollo:
#import <Foundation/Foundation.h>
#import "VehicleProtocol.h"
@interface Car : NSObject<VehicleProtocol, DriverelessProtocol>
@end
Ovviamente dovremo anche implementare in Car.m gli eventuali metodi dichiarati da DriverelessProtocol.
Metodi opzionali
Con Objective-C possiamo anche dichiarare dei metodi opzionali, ovvero dei metodi che non devono necessariamente essere implementati dalla classe conforme al protocollo.
Esaminiamo il seguente esempio:
#import <Foundation/Foundation.h>
@protocol VehicleProtocol <NSObject>
- (int)numberOfWheels;
@optional
- (int)availableFuel;
@end
Abbiamo aggiunto la definizione del metodo availableFuel
, che però è opzionale. Ciò significa che potremo implementarlo, ad esempio, nella classe Car e non nella classe Bicycle (facendo ancora riferimento agli esempi della lezione precedente).
Apriamo il file Car.m e modifichiamolo come segue:
#import "Car.h"
@implementation Car
-(int) numberOfWheels {
return 4;
}
-(int) availableFuel {
return 30;
}
@end
Se compiliamo, non ci sarà restituito alcun errore, anche se Bicycle non possiede questo nuovo metodo.
Torniamo ora al file main.m, modificandolo come segue:
@import Foundation;
#import "Car.h"
#import "Bicycle.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
id<VehicleProtocol> vehicle = [Car new];
NSLog(@"%d", [vehicle availableFuel]);
}
return 0;
}
Premendo CMD + R per eseguire il progetto, vedremo nella console di debug che verrà stampato il numero 30.
Tuttavia, se inseriamo una Bicycle all’interno della variabile vehicle
:
@import Foundation;
#import "Car.h"
#import "Bicycle.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
id<VehicleProtocol> vehicle = [Bicycle new];
NSLog(@"%d", [vehicle availableFuel]);
}
return 0;
}
otterremo un errore di runtime:
-[Bicycle availableFuel]: unrecognized selector sent to instance 0x100304620
Il compilatore ci sta dicendo che non ha trovato il metodo availableFuel
dentro Bicycle. Per scrivere codice robusto nel caso di metodi opzionali, in genere è bene effettuare un check prima di invocare un metodo opzionale, come mostrato di seguito.
@import Foundation;
#import "Car.h"
#import "Bicycle.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
id<VehicleProtocol> vehicle = [Bicycle new];
if ([vehicle respondsToSelector:@selector(availableFuel)]) {
NSLog(@"%d", [vehicle availableFuel]);
}
}
return 0;
}
In questo modo, invocando [vehicle respondsToSelector:@selector(availableFuel)]
, ci stiamo assicurando che il metodo availableFuel
sia stato effettivamente implementato.