Как мне вызвать + методы класса в Objective C без ссылки на класс?
У меня есть ряд объектов "policy", которые, на мой взгляд, было бы удобно реализовать в виде методов класса для набора классов политики. Я указал протокол для этого и создал классы для соответствия (только один показан ниже)
@protocol Counter
+(NSInteger) countFor: (Model *)model;
@end
@interface CurrentListCounter : NSObject <Counter>
+(NSInteger) countFor: (Model *)model;
@end
Затем у меня есть массив классов, которые соответствуют этому протоколу (как это делает CurrentListCounter)
+(NSArray *) availableCounters {
return [[[NSArray alloc] initWithObjects: [CurrentListCounter class], [AllListsCounter class], nil] autorelease];
}
Обратите внимание, как я использую классы как объекты (и это может быть моей проблемой - в Smalltalk классы являются объектами, как и все остальное - я не уверен, что они в Objective-C?)
Моя точная проблема - когда я хочу вызвать метод, когда я беру один из объектов политики из массива:
id<Counter> counter = [[MyModel availableCounters] objectAtIndex: self.index];
return [counter countFor: self];
Я получаю предупреждение об операторе возврата - он говорит -countFor: не найден в протоколе (поэтому предполагается, что это метод экземпляра, в котором я хочу вызвать метод класса). Однако, поскольку объекты в моем массиве являются экземплярами класса, они теперь похожи на методы экземпляров (или концептуально они должны быть).
Есть ли волшебный способ вызова методов класса? Или это просто плохая идея, и я должен просто создавать экземпляры своих объектов политики (а не использовать методы класса)?
3 ответа
Это
id <Counter> counter = [[Model availableCounters] objectAtIndex:0];
return ( [counter countFor: nil] );
Должно быть
Class <Counter> counter = [[Model availableCounters] objectAtIndex:0];
return ( [counter countFor: nil] );
В первом у вас есть экземпляр, который соответствует <Counter>
, Во втором у вас есть класс, который соответствует <Counter>
, Предупреждение компилятора верно, потому что экземпляры, которые соответствуют <Counter>
не отвечать на countFor:
Только классы делают.
Класс в Objective-C работает как экземпляр - основное различие заключается в том, что сохранение счета ничего не делает для них. Однако то, что вы храните в массиве -availableCounters, не является экземплярами (id
тип) но классы, которые имеют тип Class
, Следовательно, с определением -availableCounters, которое вы указали выше, вам нужно следующее:
Class counterClass = [[MyModel availableCounters] objectAtIndex: self.index];
return ( [counterClass countFor: self] );
Однако, вероятно, было бы лучше семантически лучше, если бы вы использовали экземпляры, а не классы. В этом случае вы можете сделать что-то вроде следующего:
@protocol Counter
- (NSUInteger) countFor: (Model *) model;
@end
@interface CurrentListCounter : NSObject<Counter>
@end
@interface SomeOtherCounter : NSObject<Counter>
@end
Тогда ваш класс модели может реализовать следующее:
static NSArray * globalCounterList = nil;
+ (void) initialize
{
if ( self != [Model class] )
return;
globalCounterList = [[NSArray alloc] initWithObjects: [[[CurrentListCounter alloc] init] autorelease], [[[SomeOtherCounter alloc] init] autorelease], nil];
}
+ (NSArray *) availableCounters
{
return ( globalCounterList );
}
Тогда ваше использование будет точно таким, как вы указали выше:
id<Counter> counter = [[Model availableCounters] objectAtIndex: self.index];
return ( [counter countFor: self] );
Оказывается, это действительно работает, и предупреждение неверно.
Таким образом, все еще остается вопрос: разумно ли это делать (использовать методы класса, если им не нужно состояние)?
И как лучше всего обработать предупреждение (мне нравится запускать предупреждение бесплатно)?
Мой единственный обходной путь состоял в том, чтобы иметь второй протокол (по сути, такой же, как первый, но объявить его на стороне экземпляра):
@protocol CounterInstance
-(NSInteger) countFor: (Model *)model;
@end
И где я получаю доступ к счетчикам, используйте его вместо этого (чтобы обмануть компилятор):
id<CounterInstance> counter = [[MyModel availableCounters] objectAtIndex: self.index];
return [counter countFor: self];
Это немного неловко, но работает. Заинтересованы во взглядах других?