Как мне вызвать + методы класса в 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];

Это немного неловко, но работает. Заинтересованы во взглядах других?

Другие вопросы по тегам