Как мне вызвать + методы класса в Objective C без ссылки на класс? - PullRequest
10 голосов
/ 31 мая 2009

У меня есть ряд объектов «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];

Я получаю предупреждение о выражении return - в нем говорится -countFor: не найдено в протоколе (поэтому предполагается, что это метод экземпляра, в котором я хочу вызвать метод класса). Однако, поскольку объекты в моем массиве являются экземплярами класса, они теперь похожи на методы экземпляров (или концептуально они должны быть).

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

Ответы [ 3 ]

20 голосов
/ 01 июня 2009

Это

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

5 голосов
/ 31 мая 2009

Класс в 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] );
0 голосов
/ 31 мая 2009

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

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

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

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

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

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

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

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

...