Как мне вызвать методы класса в Objective C без ссылки на класс?

У меня есть ряд объектов "политики", которые, как я думал, будут удобны для реализации как методы класса в наборе классов политик. Я указал протокол для этого и создал классы для соответствия (только один, показанный ниже)

@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: не найден в протоколе (поэтому его предполагается его метод экземпляра, где я хочу вызвать метод класса). Однако, поскольку объекты в моем массиве являются экземплярами класса, они теперь похожи на методы экземпляра (или концептуально они должны быть).

Есть ли волшебный способ вызова методов класса? Или это просто плохая идея, и я должен просто создавать экземпляры моих объектов политики (а не использовать методы класса)?

Ответ 1

Это

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:, только классы делают.

Ответ 2

Класс в 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] );

Ответ 3

Оказывается, он работает, и предупреждение неверно.

Итак, вопрос по-прежнему касается вопроса о целесообразности его использования (использовать методы класса, если они не нуждаются в каком-либо состоянии)?

И как лучше всего обработать предупреждение (мне нравится запускать предупреждение бесплатно)?

Мое единственное обходное решение заключалось в том, чтобы иметь второй протокол (по существу такой же, как первый, но объявить его на стороне экземпляра):

@protocol CounterInstance
-(NSInteger) countFor: (Model *)model;
@end

И когда я получаю доступ к счетчикам, используйте его вместо этого (чтобы обмануть компилятор):

id<CounterInstance> counter = [[MyModel availableCounters] objectAtIndex: self.index];
return [counter countFor: self];

Его немного неловко, но он работает. Меня интересуют мнения других?