Можно использовать следующий подход для сопоставления селекторов с типами objc, а затем позволить этой реализации выполнить поиск метода для «динамической перегрузки». Такая реализация устранит большую часть шума для вашего фактического использования (см. Демонстрацию - 8 страниц ниже):
Наше предложение:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
Наши основные виды:
MONVisitorEntry связывает класс с селектором:
MONVisitorEntry.h:
@interface MONVisitorEntry : NSObject
+ (MONVisitorEntry *)newVisitorEntryWithType:(Class)inType selector:(SEL)inSelector;
- (id)visit:(id)target parameter:(id)parameter;
- (Class)type;
@end
MONVisitorEntry.m:
@implementation MONVisitorEntry
{
@private
Class type;
SEL selector;
}
- (id)initWithType:(Class)inType selector:(SEL)inSelector
{
self = [super init];
if (0 != self) {
type = inType;
selector = inSelector;
}
return self;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"%@ - Type: %@ - SEL: %@", [super description], type, NSStringFromSelector(selector)];
}
- (NSUInteger)hash
{
return (NSUInteger)type;
}
- (Class)type
{
return type;
}
+ (MONVisitorEntry *)newVisitorEntryWithType:(Class)inType selector:(SEL)inSelector
{
return [[self alloc] initWithType:inType selector:inSelector];
}
- (id)visit:(id)target parameter:(id)parameter
{
return ([target methodForSelector:selector])(target, selector, parameter);
}
@end
MONVisitorMap - это карта MONVisitorEntry
объектов. этот тип не имеет безопасности типа - вы должны снова ввести его.
MONVisitorMap.h:
@interface MONVisitorMap : NSObject
- (void)addEntryWithType:(Class)inType selector:(SEL)inSelector;
/* perhaps you would prefer that inTarget is also held by self? in that case, you could also cache the IMPs for faster lookups. */
- (id)visit:(id)inTarget parameter:(id)inParameter;
@end
MONVisitorMap.m:
@implementation MONVisitorMap
{
@private
NSMutableSet * entries;
}
- (id)init
{
self = [super init];
if (0 != self) {
entries = [NSMutableSet new];
}
return self;
}
- (NSString *)description
{
return [[super description] stringByAppendingString:[entries description]];
}
- (void)addEntryWithType:(Class)inType selector:(SEL)inSelector
{
[entries addObject:[MONVisitorEntry newVisitorEntryWithType:inType selector:inSelector]];
}
- (id)visit:(id)inTarget parameter:(id)inParameter parameterClass:(Class)inParameterClass
{
MONVisitorEntry * entry = 0;
for (MONVisitorEntry * at in entries) {
if (inParameterClass == at.type) {
entry = at;
}
}
if (0 != entry) {
return [entry visit:inTarget parameter:inParameter];
}
Class superclass = class_getSuperclass(inParameterClass);
if (0 == superclass) {
assert(0 && "exhausted class hierarchy!");
return 0;
}
return [self visit:inTarget parameter:inParameter parameterClass:superclass];
}
- (id)visit:(id)inTarget parameter:(id)inParameter
{
return [self visit:inTarget parameter:inParameter parameterClass:[inParameter class]];
}
@end
Демо-версия:
Создайте несколько типов тестов (добавьте сюда несколько файлов .m
):
@interface Animal : NSObject
@end
@implementation Animal
@end
@interface Dog : Animal
@end
@implementation Dog
@end
@interface Greyhound : Dog
@end
@implementation Greyhound
@end
@interface Boxer : Dog
@end
@implementation Boxer
@end
@interface Squirrel : Animal
@end
@implementation Squirrel
@end
@interface Tapir : Animal
@end
@implementation Tapir
@end
Создать посетителя:
MONZoo.h:
@interface MONZoo : NSObject
/* our abstract "visit" entry, which introduces type safety: */
- (void)exhibit:(Animal *)inAnimal;
@end
MONZoo.m:
@implementation MONZoo
{
@private
MONVisitorMap * visitorMap;
}
static NSString * Message(NSString * inMessage, id inInstance) {
return [NSString stringWithFormat:@"Message: \"%@\" -- Instance: %@", inMessage, inInstance];
}
// Here's where you implement a method for an animal:
- (id)visitAnimal:(Animal *)p { return Message(@"What animal is this?", p); }
- (id)visitDog:(Dog *)p { return Message(@"What's up, Dog!", p); }
- (id)visitGreyhound:(Greyhound *)p { return Message(@"Ohhhhh a Greyhound!!", p); }
- (id)visitTapir:(Tapir *)p { return Message(@"What does it cost to feed this?", p); }
// Here's where you map methods to animals:
+ (MONVisitorMap *)newVisitorMap
{
MONVisitorMap * map = [MONVisitorMap new];
[map addEntryWithType:[Dog class] selector:@selector(visitDog:)];
[map addEntryWithType:[Greyhound class] selector:@selector(visitGreyhound:)];
[map addEntryWithType:[Tapir class] selector:@selector(visitTapir:)];
[map addEntryWithType:[Animal class] selector:@selector(visitAnimal:)];
/* omitting the Boxer (Dog) to demonstrate pseudo-overload */
return map;
}
- (id)init
{
self = [super init];
if (0 != self) {
visitorMap = [[self class] newVisitorMap];
}
return self;
}
- (NSString *)description
{
return [[super description] stringByAppendingString:[visitorMap description]];
}
- (void)exhibit:(Animal *)inAnimal
{
NSLog(@"Visiting Exhibit: %@", [visitorMap visit:self parameter:inAnimal]);
}
@end
Теперь попробуйте:
int main(int argc, const char * argv[]) {
@autoreleasepool {
MONZoo * zoo = [MONZoo new];
NSLog(@"Hello, Zoo! -- %@", zoo);
[zoo exhibit:[Dog new]];
[zoo exhibit:[Greyhound new]];
[zoo exhibit:[Squirrel new]];
[zoo exhibit:[Tapir new]];
[zoo exhibit:[Boxer new]];
}
return 0;
}
Что приводит к нашей поездке в зоопарк:
2012-02-21 04:38:58.360 Visitor[2195:403] Hello, Zoo! -- <MONZoo: 0x10440ed80><MONVisitorMap: 0x1044143f0>{(
<MONVisitorEntry: 0x104414e00> - Type: Dog - SEL: visitDog:,
<MONVisitorEntry: 0x104410840> - Type: Greyhound - SEL: visitGreyhound:,
<MONVisitorEntry: 0x104415150> - Type: Animal - SEL: visitAnimal:,
<MONVisitorEntry: 0x104415130> - Type: Tapir - SEL: visitTapir:
)}
2012-02-21 04:38:58.363 Visitor[2195:403] Visiting Exhibit: Message: "What's up, Dog!" -- Instance: <Dog: 0x7f9a29d00120>
2012-02-21 04:38:58.363 Visitor[2195:403] Visiting Exhibit: Message: "Ohhhhh a Greyhound!!" -- Instance: <Greyhound: 0x7f9a29e002c0>
2012-02-21 04:38:58.364 Visitor[2195:403] Visiting Exhibit: Message: "What animal is this?" -- Instance: <Squirrel: 0x104416470>
2012-02-21 04:38:58.364 Visitor[2195:403] Visiting Exhibit: Message: "What does it cost to feed this?" -- Instance: <Tapir: 0x7f9a29d00120>
2012-02-21 04:38:58.364 Visitor[2195:403] Visiting Exhibit: Message: "What's up, Dog!" -- Instance: <Boxer: 0x1044140a0>
Примечания:
- Принесите свое собственное обнаружение ошибок;)
- Извините за быстрое написание
- Извините за отсутствие документов
- Скомпилировано с ARC
- Конечно, есть варианты, которые вы можете сделать, чтобы удовлетворить ваши потребности.
- Возможно, это не самая правильная форма шаблона, но вы можете легко представить это, добавив один или два метода с помощью этой программы, если вы предпочитаете такой подход.