Главная iPhone Mac OS X Форум О себе

Как работает механизм сообщений

Компилятор переводит каждую посылку сообщения, то есть конструкцию вида [object msg] в вызов функции objc_msgSend. Эта функция в качестве своего первого параметра принимает указатель на объект-получатель сообщения, в качестве второго параметра выступает т. н. селектор, служащий для идентификации посылаемого сообщения. Если в сообщении присутствуют аргументы, то они также передаются objc_msgSend как третий, четвёртый и т. д. параметры.

Каждый объект Objective-C содержит в себе атрибут isa — указатель на class object для данного объекта. class object автоматически создается компилятором и существует как один экземпляр, на который через isa ссылаются все экземпляры данного класса.

Каждый class object обязательно содержит в себе указатель на class object для родительского класса (superclass) и dispatch table. Последняя представляет из себя словарь, сопоставляющий селекторам сообщений фактические адреса реализующих их методов (функций).

Таким образом, функция objc_msgSend ищет метод с данным селектором в dispatch table для данного объекта. Если его там нет, то поиск продолжается в dispatch table для его родительского класса и т. д.

Если метод (то есть соответствующая ему функция) находится, то осуществляется его вызов с передачей всех необходимых аргументов.

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

Если объект поддерживает forwardInvocation:, то он может либо сам обработать посылаемое сообщение, либо переслать другому объекту для обработки:


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

Для ускорения поиска сообщений по dispatch table используется кэширование, позволяющее заметно снизить затраты на пересылку сообщений. Также облегчает поиск метода по таблицам использование так называемых селекторов вместо обычных имен. Обычно селектор представляет собой 32-битовую величину, позволяющую однозначно идентифицировать метод.

Тип селектора обозначается как SEL и существует ряд функций и конструкций, позволяющих осуществлять преобразование имени в селектор и обратно.

Так для получения селектора сообщения непосредственно по имени служит конструкция @selector():


SEL setWidth = @selector(setWidth:);
SEL setPos   = @selector(setX:y:);

Для получения селектора по строке символов (на этапе выполения) и перевода селектора в строку служат функции NSSelectorFromString и NSStringFromSelector:


SEL        setWidth   = NSSelectorFromString ( @"setWidth:" );
NSString * methodName = NSStringFromSelector  ( setPos );

Мощная поддержка метаинформации в Objective-C позволяет прямо на этапе выполения проверить поддерживает ли объект метод с данным селектором при помощи посылки ему сообщения respondsToSelector::


if ( [anObject respondsToSelector: @selector(setWidth:)] )
     [anObject setWidth: 200.0];

Довольно легко можно послать сообщение, соответствующее данному селектору (без аргументов, с одним, двумя или тремя аргументами), при помощи метода performSelector:, performSelector:withObject:, performSelector:withObject:withObject: и performSelector::withObject:withObject::withObject:.


[myObject performSelector:sel withObject: nil];

Обратите внимание, что методы performSelector: всегда возвращают значение типа id.

Можно получить класс для данного объекта, послав ему сообщение class. Это сообщение возвращает класс в виде указателя на объект типа Class.


Class    * cls     = [anObject class];
NSString * clsName = NSStringFromClass ( cls );

С другой стороны также можно легко получить соответствующий class object по имени класса:


Class * cls = NSClassFromString ( clsName );

Каждый метод фактически представляет собой функцию с двумя невидимыми аргументами — self и _cmd.

Первый является аналогом this, то есть указывает на сам объект — получатель сообщения. Второй — содержит селектор данного метода.

Аргумент self может использоваться для посылки сообщений самому себе, как например в следующем методе:


— (float) area
{
   return [self width] * [self height];
}

Однако кроме self есть ещё одна величина, которой могут посылаться сообщения — super. На самом деле super не является нормальной переменной — это всего лишь ещё одно обозначение для указателя на текущий объект. Но при посылке сообщения super поиск метода начинается не с dispatch table текущего объекта, а с dispatch table родительского объекта.

Таким образом, посылая сообщения super мы тем самым вызываем старые версии методов, переопределенные данным классом.

В языке Objective-C можно по селектору метода получить адрес реализующей его функции (именно как функции языка С).

Такая функция отличается от описания метода только вставкой в начало списка аргументов двух дополнительных параметров — указателя на сам объект (self) и селектора данного метода (_cmd).

Послав объекту сообщение methodForSelector: мы получаем в ответ адрес реализующей этот метод функции.


typedef float (*WidthFunc)( id, SEL );
typedef void  (*SetWidthFunc)( id, SEL, float );

WidthFunc    widthFunc    = (WidthFunc)    [myRect methodForSelector: @selector (width)];
SetWidthFunc setWidthFunc = (SetWidthFunc) [myRect methodForSelector: @selector (setWidth:)];

(*SetWidthFunc)( myRect, @selector (setWidth:), 27.5f );

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

Взять с http://steps3d.narod.ru

Комментарий

Комментарии

steps3D 7.07.2009 9:54

Объясните пожалуйста почему кусок моей статьи об objective-c слово в слово появился здесь со ссылкой нне на мой сайт, а на сайт, укравший этот текст у меня ?

ответить
Evgeniy Krysanov 7.07.2009 11:01

По той причине, что я договаривался с автором сайта devmac.ru, я не знал, что это не его статья; у него не было никакой ссылки. Сейчас исправлю источник или уберу статью, если вы пожелаете.

ответить
Nitr0geN 8.07.2009 12:09

Статья была взята отсюда: http://ru.wikipedia.org/wiki/Objective-C

О том, что эта статья того человека, что написал выше я не курсе. Мне предоставили доказательства, я тоже у себя поправил.

ответить
oxigen 8.07.2009 16:31

Вы уверены, что это работает? performSelector:withObject:withObject:

Насколько я знаю, селектору можно передать только один объект. Если я не прав, то можно увидеть пример кода? У меня не получилось так сделать (

ответить
steps3D 8.07.2009 16:50

Собственно вот описание протокола https://developer.apple.com/documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/Reference/NSObject.html#//apple_ref/occ/intfm/NSObject/performSelector: Там указаны три варианта performSelector: performSelector:withObject: performSelector:withObject:withObject

Насколько я помню эти вызовы живут чуть ли не времен NextStep’а Прямо сейчас проверить работу не могу :(((

ответить
oxigen 8.07.2009 18:17

Сорь, протупил Смотрел для iPhon а не для мака

ответить

Форма комментирования для «Как работает механизм сообщений»

Обязательное поле. Не больше 30 символов.

Обязательное поле

captcha image Пожалуйста, введите символы, которые вы видите на изображении

Карта Сайта