CocoaDev.ru
Блог о Cocoa разработке
Блог о Cocoa разработке
июля 24
Итак, задача.
Есть клиентское приложение, которое имеет постоянное сокетное соединение. Сервер, периодически, посылает клиентскому приложению набор команд в виде XML. Именем команды является имя ноды в XML. Задача клиентского приложения, обработать эти команды.
В принципе, задача не сложная, если бы не большое колическтво команд. В таком случае, метод-обработчик команд представлял бы из себя гигантскую сущность, состоящую из одних IF … ELSE что не дает красоты коду.
Мы поступим подругому. Мы сделаем коллекцию, имеющую тип ЗНАЧЕНИЕ-КЛЮЧ, в которой будем хранить селекторы, а в качестве ключа будет имя команды.
Когда приходит пакет с командой от сервера, он десериализуется в некую абстрактную сущность, класс, Command, имеющую иерархическую структуру, т.е. сабкоманды, а также аргументы.
Т.е. по сути, мы можем сделать для каждой команды свой специфический обработчик, но у всех обработчиков будет одна и та же сигнатура.
Например, у нас есть команда, точнее ответ сервера на команду login. В данном случае мы сделаем метод-обработчик: -(void) processLoginResponse:(Command *)command;
Этот метод обработает пришедший ответ от сервера и выполнит все необходимые проверки и действия. Что внутри этого метода будет дальше, писать не буду. Задача сделать так, чтобы обработчик сетевых пакетов, вытащил ссылку на метод-обработчик команды login и исполнил его, передав параметром команду.
Итак, в главном классе приложения, который и будет заниматься всем этим, назовем его Engine, создадим метод инициализации коллекции методов:
- (void)initCommandsHolder
{
commandsHolder = [[NSMutableDictionary alloc] init];
//commandsHolder - член класса Engine, собственно и будет той коллекцией
//Создаем селектор для нашего метода-обработчика
SEL theSelector = @selector(processLoginResponse:);
//Описываем сигнатуру для нашего метода
NSMethodSignature * aSignature = [Engine instanceMethodSignatureForSelector:theSelector];
//Создаем инстанцию, которая будет стартовать метод и храниться в коллекции
NSInvocation * anInvocation = [NSInvocation invocationWithMethodSignature:aSignature];
//Задаем этой инстанции полученный нами селектор
[anInvocation setSelector:theSelector];
//Сохраняем инстанцию в коллекции
[commandsHolder setObject:anInvocation forKey:@"login"];
}
Итак, у нас есть коллекция, со ссылкой на один метод-обработчик. Далее перейдем к методу, который обрабатывает пакеты от сервера:
- (void) processResponse:(NSString *)response
{
//Десериализуем пакет в инстанс класса Command для дальнейшего использования
Command * command = [[Command alloc]initWithXML:response];
//Проверяем, есть ли в коллекции обработчики
if([commandsHolder count] > 0)
{
//Получаем инстанцию стартера для метода-обработчика по имени команды
NSInvocation * anInvocation = [commandsHolder objectForKey:[command name]];
//Проверяем, удалось ли получить инстанцию
if(anInvocation)
{
//Устанавливаем инстанции целевой класс, у которого есть этот метод-обработчик
[anInvocation setTarget:self];
//Задаем аргумент для запуска
//Индекс равен 2, потому как индексы 0 и 1 являются зарезервированными и скрытыми
//Индекс 0 содержит ссылку на инстанцию стартера, а индекс 1 ссылку на сигнатуру метода
[anInvocation setArgument:&command atIndex:2];
//Запускаем метод-обработчик
[anInvocation invoke];
}
}
}
Все готово. Пока у нас в коллекции один метод, но мы с легкостью можем добавить множество методов и избавить себя от кучи IF ELSE, которые мозолят глаза.