что делает компилятор, когда находит [super msg];

Я прочитал главу Apple «обмен сообщениями» из программирования с целью - c и получил несколько вопросов о себе и супер. Насколько мне известно, когда компилятор находит какое-либо сообщение, он переводит его в objc_msgSend с двумя скрытыми параметрами - получатель, селектор и переменные аргументы для селектора. например [self test] будет примерно так:

objc_msgSend(self, @selector(test));

если в таблице отправки получателя нет реализации метода, то функция попытается найти реализацию в суперклассах. super - это просто флаг для компилятора, чтобы начать поиск реализации метода в суперклассе текущего объекта, и в документации Apple говорится, что когда компилятор находит "super", он переводит его примерно так:

struct objc_super mySuperClass = {
    self,
    [self superclass]
};
objc_msgSendSuper(&mySuperClass, @selector(forwardedMethod));

Я сделал проект с 3 классами, каждый из которых наследуется от другого.

@interface FirstClass : NSObject
- (void)forwardMethod;
@end

@interface SecondClass : FirstClass
@end

@interface ThirdClass : SecondClass
@end

Я создал экземпляр третьего класса в моем корневом контроллере представления и вызвал его метод под названием «forwardMethod». Реализация :

//First Class
- (void)forwardMethod {
   NSLog(@"Base class reached");
}

//SecondClass imp
- (void)forwardMethod {
   NSLog(@"second class");
   [super forwardMethod];
}

//ThirdClass imp
- (void)forwardMethod {
   NSLog(@"third class");
   [super forwardMethod];
}

Все работает нормально. Но потом я решил интерпретировать компилятор:

//First Class
- (void)forwardMethod {
   NSLog(@"Base class reached");
}

//SecondClass imp
- (void)forwardMethod {
   NSLog(@"second class");
   struct objc_super mySuperClass = {
      self,
      [self superclass]
   };

   objc_msgSendSuper(&mySuperClass, @selector(forwardMethod));
}

//ThirdClass imp
- (void)forwardMethod {
   NSLog(@"third class");
    struct objc_super mySuperClass = {
        self,
        [self superclass]
    };

    objc_msgSendSuper(&mySuperClass, @selector(forwardMethod));
}

Что приводит к рекурсивному вызову второго класса «forwardMethod». Я создаю структуру в 'forwardMethod' во втором классе, используя self и [self superclass], но self является третьим классом, и
мой суперкласс всегда будет "вторым классом". Может быть, я делаю что-то не так, но как мне добраться до «метода пересылки» базового класса?


person Yurii Romanchenko    schedule 30.06.2013    source источник


Ответы (2)


Примечание. Только в образовательных целях (и это хорошо!), не используйте в рабочем коде!

Ты тоже самый там, всего на один класс меньше...

Чтобы понять, почему вы получаете рекурсию, подумайте, как можно найти суперкласс, используя информацию, доступную во время времени компиляции и во время времени выполнения.

Во время выполнения значение self является ссылкой на текущий объект, вы можете использовать self для поиска класса объекта - в вашем примере self является объектом типа ThirdClass.

Теперь значение self не меняется при вызове методов, поэтому в вашем примере даже в FirstClass forwardMethod значение self является ссылкой на объект типа ThirdClass. Таким образом, self позволяет вам найти тип объекта, но не говорит вам, где вы в данный момент выполняете метод в его цепочке наследования, поэтому сам по себе он не может сказать вам, какой следующий класс в этой цепочке.

Так что учитывайте время компиляции. При компиляции SecondClass компилятор знает, что суперклассом является FirstClass, поэтому вызов super является вызовом метода в FirstClass (за исключением предостережения ниже). Таким образом, компилятор может использовать self и [FirstClass class] для определения объекта среды выполнения, для которого следует вызывать метод, и класса времени компиляции, с которого следует начать поиск метода (поскольку любой поиск метода — это поиск, начинающийся с класса и далее по цепочке наследования до тех пор, пока не будет найдена реализация). Итак, в вашем примере кода у вас был только один метод:

@implementation SecondClass

- (void)forwardMethod
{
   NSLog(@"second class");
   struct objc_super mySuperClass =
   {
      self,
      [FirstClass class]
   };

   objc_msgSendSuper(&mySuperClass, @selector(forwardMethod));
}

Если вы используете это, ваш код будет работать. Но...

... это предостережение, упомянутое выше. Objective-C позволяет изменять цепочку наследования во время выполнения с помощью перебора классов, поэтому компилятор обычно не может полагаться на цепочку наследования во время компиляции для разработки суперкласса. Итак, что он может использовать? Что ж, при компиляции исходного кода для определенного метода он знает, к какому классу принадлежит этот метод, поэтому он может скомпилировать этот класс методов в код, чтобы найти суперкласс и использовать код, который начинает поиск метода в next< /em> в цепочке наследования runtime. На самом деле это то, что компилятор сделает для вашего кода, на тот случай, если вы использовали смешение классов:

@implementation SecondClass

- (void)forwardMethod
{
   NSLog(@"second class");
   struct objc_super mySuperClass =
   {
      self,
      [SecondClass class]
   };

   objc_msgSendSuper2(&mySuperClass, @selector(forwardMethod));
}

Обратите внимание, что компилятор передает текущий класс времени компиляции ([SecondClass class]) и вызывает objc_msgSendSuper2 для выполнения поиска, который находит первый метод в цепочке наследования времени выполнения, который находится после SecondCLassm, тогда как `objc_msgSendSuper начнет поиск с SecondClass сам.

Получайте удовольствие, но не используйте это в общем коде (если только у вас нет очень, очень, очень... очень серьезной причины ;-))

person CRD    schedule 30.06.2013
comment
спасибо за такое глубокое и ясное объяснение :) еще раз большое спасибо: D не могу выразить, как я благодарен - person Yurii Romanchenko; 01.07.2013
comment
так почему там objc_msgSendSuper, если вместо него всегда используется objc_msgSendSuper2? - person newacct; 01.07.2013
comment
@newacct - Извините, не могу дать вам окончательный ответ. Я не знаю, что компилятор всегда использует objc_msgSendSuper2. Существование обоих методов может быть историческим. - person CRD; 01.07.2013
comment
Мне было любопытно, как можно изменить цепочку наследования, кроме использования class_setSuperclass(), который устарел? - person user102008; 23.09.2013
comment
@ user102008 - Объект Objective-C представлен структурой C (struct), и вы можете изменить все, что захотите, в структуре C... - person CRD; 23.09.2013

В вашем SecondClass эта структура будет заполнена точно таким же содержимым, как и ваш ThirdClass:

   struct objc_super mySuperClass = {
      self,
      [self superclass]
   };

self имеет одинаковое значение в обоих случаях, и, таким образом, [self superclass] всегда будет SecondClass при вызове из экземпляра ThirdClass (даже если фактическая реализация — код — находится во SecondClass). В том, что выдает компилятор, есть немного больше волшебства (objc_msgSendSuper довольно прямолинеен), поскольку он должен выдавать ссылку на класс так, что даже такие вещи, как поза и/или манипулирование указателем isa — плохой программист, не пончик — все еще работать, как ожидалось. Я давно не вникал в детали, чтобы точно знать, как это работает.

исходный код среды выполнения и .

person bbum    schedule 30.06.2013
comment
+1 Я искал какую-то встроенную функцию для текущего класса, которая знает, в каком классе на самом деле определена функция, чтобы вы могли получить ссылку на фактический суперкласс, видимый с точки зрения функции, но не смог его найти. Но мне стало интересно, буду искать :) - person Joachim Isaksson; 30.06.2013