Как построить NSArray (или NSMutableArray) методов класса в Objective-C?

Я пытаюсь создать NSArray методов в Objective-C.

(То, что я пытаюсь выполнить здесь, в C) выглядит следующим образом:

typedef (void)(*handler)(int command);

void handleCommandA(void) { ... }
void handleCommandB(void) { ... }

static const handler handler_table[10] = {
    handleCommandA, handleCommandB, handleCommandC
};

Мне нужно переносить это на Objective-C, и я не знаю, как постройте массив указателей функций (в мире Objective-C методы класса) во время компиляции.

В Objective-C у меня есть следующее.

- (void)handleCommandA { ... }
- (void)handleCommandB { ... }

/* Now how to add above 2 functions into NSArray? */
NSArray *handler_table = [NSArray arrayWithObjects:... ];  /* This doesn't seem to work. */

Ответ 1

Использовать NSValue.

Например:

NSArray* handlers = [NSArray arrayWithObjects:[NSValue valueWithPointer:handleA] ... ];

затем для доступа:

handleptr* handle = (handlerptr*)[[handlers objectAtIndex:0] pointerValue];
handle(foo_bar);

Ответ 2

Проблема заключается в том, что для привязки этих функций вы должны использовать ключевое слово selector, которое возвращает тип SEL. Это тип указателя, тогда как NSArray хранит объекты.

Таким образом, у вас есть три варианта:

  • Используйте обычный массив C-типа
  • Сложите функции в производный класс NSObject, который будет их вызывать.
  • Использовать протокол.

Вторая, вероятно, лучше, и для этого вы можете использовать класс NSValue для хранения результатов селектора. Например;

NSValue* selCommandA = [NSValue valueWithPointer:@selector(handleCommandA:)];
NSValue* selCommandB = [NSValue valueWithPointer:@selector(handleCommandB:)];

NSArray *handler_table = [NSArray arrayWithObjects:selCommandA, selCommandB, nil ];

Когда вы получили правильную запись из массива, чтобы конвертировать назад, вы бы сделали:

SEL mySelector = [selCommand pointerValue];
[someObject performSelector:mySelector];

(Примечание. Я предполагаю, что из вашего синтаксиса objective-c они предназначены для использования в качестве методов для объекта, а не для глобальных функций. Если вы хотите использовать их глобально, вы должны написать их так, как вы бы С.)

Другой вариант - формализовать методы команд в протокол. Это позволяет вам писать функциональные возможности, которые будут работать на любом объекте, который реализует этот протокол, и компилятор обеспечит большую проверку, чем если бы вы просто вызывали селекторов.

например.

// some header
@protocol CommandHandler
@required
-(void) handleCommandA;
-(void) handleCommandB;
@end

// some other header
@interface someClass : NSObject<CommandHandler>
{
// you will receive compiler warnings if you do not implement the protocol functions
}

Затем обрабатывается код обработки и отправки для работы с объектами типа "CommandHandler". Например

-(void) registerForCommands:(CommandHandler*)handler

Ответ 3

В Objective-C вы не обходите методы; вы проходите селекторами, которые в основном являются каноническими именами методов. Затем, чтобы объект ответил на сообщение селектора, вы отправляете его performSelector:. Например:

NSString *exampleString = [NSString stringWithString:@"Hello"];
SEL methodName = @selector(stringByAppendingString:);
  // ^This is the selector. Note that it just represents the name of a
  //  message, and doesn't specify any class or implementation
NSString *combinedString = [exampleString performSelector:methodName withObject:@" world!"];

Вы хотите создать массив NSStrings, содержащий имена интересующих вас селекторов. Для этого вы можете использовать функцию NSStringFromSelector(). Затем, когда вы хотите их использовать, вызовите NSSelectorFromString() в строках, чтобы вернуть исходный селектор и передать его соответствующему объекту performSelector:. (Как показано в примере выше, приемник не закодирован в селекторе - просто имя метода - так что вам, возможно, потребуется также сохранить приемник.)