Передача сообщения

Отправка сообщения к объекту, не обрабатывающему то сообщение, является ошибкой. Однако прежде, чем объявить об ошибке, система во время выполнения дает принимающему объекту второй шанс обработать сообщение.

Передача

Если Вы отправляете сообщение в объект, не обрабатывающий то сообщение, прежде, чем объявить об ошибке, время выполнения отправляет объект a forwardInvocation: сообщение с NSInvocation возразите как собственный параметр — NSInvocation объект инкапсулирует исходное сообщение и параметры, переданные с ним.

Можно реализовать a forwardInvocation: метод, чтобы дать ответ по умолчанию на сообщение или избежать ошибки некоторым другим способом. Поскольку его имя подразумевает, forwardInvocation: обычно используется для передачи сообщения к другому объекту.

Для наблюдения объема и намерения передачи вообразите следующие сценарии: Предположим, во-первых, что Вы разрабатываете объект, который может реагировать на вызванное сообщение negotiate, и Вы хотите, чтобы его ответ включал ответ другого вида объекта. Вы могли выполнить это легко путем передачи a negotiate обменивайтесь сообщениями к другому объекту где-нибудь в организации negotiate метод Вы реализуете.

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

Даже если Ваш класс не может наследоваться negotiate метод, можно все еще «занять» его путем реализации версии метода, просто передающего сообщение на экземпляр другого класса:

- (id)negotiate
{
    if ( [someOtherObject respondsTo:@selector(negotiate)] )
        return [someOtherObject negotiate];
    return self;
}

Этот способ сделать вещи мог стать немного громоздким, особенно если бы было много сообщений, которые Вы хотели свой объект передать другому объекту. Необходимо было бы реализовать один метод для покрытия каждого метода, который Вы хотели занять от другого класса. Кроме того, было бы невозможно обработать случаи, где Вы не знали, в то время, когда Вы записали код, полный набор сообщений, которые Вы могли бы хотеть передать. Тот набор мог бы зависеть от событий во время выполнения, и это могло бы измениться как новые методы, и классы реализованы в будущем.

Второй шанс предложен a forwardInvocation: сообщение предоставляет менее оперативное решение этой проблемы, и один это динамично, а не статично. Это работает как это: Когда объект не может реагировать на сообщение, потому что он не имеет метода, соответствующего селектор в сообщении, система во время выполнения сообщает объекту путем отправки ему a forwardInvocation: сообщение. Каждый объект наследовал a forwardInvocation: метод от NSObject класс. Однако NSObjectверсия метода просто вызывает doesNotRecognizeSelector:. Путем переопределения NSObjectверсия и реализация Вашего собственного, можно использовать в своих интересах возможность что forwardInvocation: сообщение обеспечивает для передачи сообщений к другим объектам.

Передавать сообщение, весь a forwardInvocation: метод должен сделать:

Сообщение может быть отправлено с invokeWithTarget: метод:

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    if ([someOtherObject respondsToSelector:
            [anInvocation selector]])
        [anInvocation invokeWithTarget:someOtherObject];
    else
        [super forwardInvocation:anInvocation];
}

Возвращаемое значение сообщения, которое это передается, возвращается к исходному отправителю. Все типы возвращаемых значений могут быть поставлены отправителю, включая ids, структуры и двойная точность числа с плавающей точкой.

A forwardInvocation: метод может действовать как центр распределения для нераспознанных сообщений, распределяя их к различным получателям. Или это может быть станция передачи, отправив все сообщения тому же месту назначения. Это может перевести одно сообщение в другого, или просто «глотать» некоторые сообщения, таким образом, нет никакого ответа и никакой ошибки. A forwardInvocation: метод может также консолидировать несколько сообщений в единственный ответ. Что forwardInvocation: делает до конструктора. Однако возможность, это предусматривает соединение объектов в передающей цепочке, открывает возможности для проектирования программы.

Для получения дополнительной информации о передаче и вызовах, посмотрите NSInvocation спецификация класса в ссылке платформы Основы.

Передача и множественное наследование

Передача наследования имитаторов, и может использоваться для кредитования некоторых эффектов множественного наследования к программам Objective C. Как показано на рисунке 5-1, объект, реагирующий на сообщение путем передачи, это, кажется, занимает или «наследовало» реализацию метода, определенную в другом классе.

  Передача рисунка 5-1

На этой иллюстрации, экземпляре класса Воина вперед a negotiate обменивайтесь сообщениями к экземпляру класса Дипломата. Воин, будет казаться, будет согласовывать как Дипломат. Это, будет казаться, ответит на negotiate сообщение, и для всех практических целей, это действительно отвечает (несмотря на то, что это - действительно Дипломат, это выполняет работу).

Объект, передающий сообщение таким образом, «наследовал» методы от двух ответвлений иерархии наследования — ее собственное ответвление и тот из объекта, реагирующего на сообщение. В примере выше, появляется, как будто класс Воина наследовался от Дипломата, а также его собственного суперкласса.

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

Суррогатные объекты

Передача не только подражает множественному наследованию, она также позволяет разработать легкие объекты, представляющие или «покрывающие» более существенные объекты. Заместитель помогает для другого объекта и направляет сообщения к нему.

Прокси, обсужденный в “Удаленном Обмене сообщениями” в Языке программирования Objective C, является таким заместителем. Прокси заботится об административных подробных данных передачи сообщений к дистанционному приемному устройству, значения аргументов проверки скопированы и получены через соединение и т.д. Но это не пытается еще сделать много; это не копирует функциональность удаленного объекта, но просто дает удаленному объекту локальный адрес, место, где это может получить сообщения в другом приложении.

Другие виды суррогатных объектов также возможны. Предположим, например, что у Вас есть объект, управляющий большим количеством данных —, возможно, он создает сложное изображение или читает содержание файла на диске. Установка этого объекта могла быть длительной, таким образом, Вы предпочитаете делать это лениво — когда действительно необходимо или когда системные ресурсы временно неактивны. Одновременно, Вам нужен, по крайней мере, заполнитель для этого объекта для других объектов в приложении для функционирования должным образом.

При этом обстоятельстве Вы могли первоначально создать, не законченный объект, но легкий заместитель для него. Этот объект мог сделать некоторые вещи самостоятельно, те, которые отвечают на вопросы о данных, но главным образом это просто содержало бы место для большего объекта и, когда время настало, передайте сообщения к нему. Когда заместитель forwardInvocation: метод сначала получает сообщение, предназначенное для другого объекта, он гарантировал бы, что объект существовал и создаст его, если он не сделал. Все сообщения для большего объекта проходят через заместителя, таким образом, насколько остальная часть программы затронута, заместитель и больший объект были бы тем же.

Передача и наследование

Несмотря на то, что передавая наследование имитаторов, NSObject класс никогда не путает два. Методы как respondsToSelector: и isKindOfClass: смотрите только на иерархию наследования, никогда в передающей цепочке. Если, например, объект Воина спрашивают, отвечает ли он на a negotiate сообщение,

if ( [aWarrior respondsToSelector:@selector(negotiate)] )
    ...

ответ NO, даже при том, что это может получить negotiate сообщения без ошибки и реагируют на них, в некотором смысле, путем передачи их Дипломату. (См. рисунок 5-1.)

Во многих случаях, NO правильный ответ. Но это может не быть. При использовании передачи, чтобы установить суррогатный объект или расширить возможности класса, передающий механизм должен, вероятно, быть столь же прозрачным как наследование. Если Вы захотите свои объекты действовать, как будто они действительно наследовали поведение объектов, к которым они передают сообщения, то необходимо будет повторно реализовать respondsToSelector: и isKindOfClass: методы для включения алгоритма передачи:

- (BOOL)respondsToSelector:(SEL)aSelector
{
    if ( [super respondsToSelector:aSelector] )
        return YES;
    else {
        /* Here, test whether the aSelector message can     *
         * be forwarded to another object and whether that  *
         * object can respond to it. Return YES if it can.  */
    }
    return NO;
}

В дополнение к respondsToSelector: и isKindOfClass:, instancesRespondToSelector: метод должен также зеркально отразить алгоритм передачи. Если протоколы используются, conformsToProtocol: метод должен аналогично быть добавлен к списку. Точно так же, если объект передает какие-либо удаленные сообщения, он получает, он должен иметь версию methodSignatureForSelector: это может возвратить точные описания методов, в конечном счете реагирующих на переадресованные сообщения; например, если бы объект в состоянии передать сообщение своему заместителю, Вы реализовали бы methodSignatureForSelector: следующим образом:

- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
    NSMethodSignature* signature = [super methodSignatureForSelector:selector];
    if (!signature) {
       signature = [surrogate methodSignatureForSelector:selector];
    }
    return signature;
}

Вы могли бы рассмотреть помещение алгоритма передачи где-нибудь в частном коде и иметь все эти методы, forwardInvocation: включенный, вызовите его.

Методы, упомянутые в этом разделе, описаны в NSObject спецификация класса в ссылке платформы Основы. Для получения информации о invokeWithTarget:, посмотрите NSInvocation спецификация класса в ссылке платформы Основы.