Вызов супер в реализации блока Objective-C

Вызывается метод супер, поддерживаемый в реализации блока Objective-C?

Когда я вызывал метод на супер, была бы выбрана ошибка EXC_BAD_ACCESS, но как только я изменил эти вызовы с [super methodToCall] на [self methodToCall], и пусть сообщение движется вверх по цепочке ответчиков, в которой он работал нормально.

В экземпляре класса, в котором существует блок, нет реализации -methodToCall, но в суперклассе (то есть классе, на который он наследуется) есть один.

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

Примечание: код реализации блока вызывается до нескольких секунд после того, как блок хранится в свойстве, свойство использует copy, поэтому я не думаю, что это проблема с жизненным циклом блока, что все выглядит хорошо. Кроме того, это было только сбой на устройстве iPhone (3G), но он работал без сбоев в iPhone Simulator.

Результаты в EXC_BAD_ACCESS:

[self retrieveItemsForId:idString completionHandler:^(NSError *error) {
 if (!error) {
  [super didRetrieveItems];
 } else {
  [super errorRetrievingItems];
 }
}];

Работает отлично, реализации -didRetrieveItems и -errorRetrievingItems находятся в суперклассе.

[self retrieveItemsForId:idString completionHandler:^(NSError *error) {

 if (!error) {
  [self didRetrieveItems];
 } else {
  [self errorRetrievingItems];
 }
}];

Ответ 1

Технически это проблема с временем выполнения Objective-C, и основная механика того, как работают вызовы super. В основном они захватывают как объект, который является получателем сообщения (self во всех случаях), так и класс, который реализует конкретную версию метода (суперкласс класса, в котором реализуется реализация метода). Потому что большая часть подготовки к такому сообщению отправляется во время компиляции, а не во время выполнения, я не удивлюсь, если он плохо взаимодействует с блоками.

Я бы посмотрел, остается ли self, когда сообщение будет отправлено. Обычно любые объекты, на которые делается ссылка в блоке, автоматически сохраняются. Так как super работает несколько иначе, это может означать, что self не сохраняется, как и следовало ожидать. Один простой способ проверить это - использовать вызовы на super, как изначально написанные, и просто пропустить объект, называемый self, и посмотреть, работает ли он. Если это окажется проблемой, вам может потребоваться вставить фиктивную ссылку на self в блоке, чтобы получить автоматическое управление памятью.

В самом строгом смысле, однако, я не уверен, что вы можете зависеть от этого, работая вечно и всегда. Хотя блоки могут захватывать текущее состояние времени выполнения, на самом деле не имеет смысла (с точки зрения ООП), чтобы они могли разбить инкапсуляцию и вызывать реализацию суперкласса, поскольку иерархический уровень, на котором реализуются методы, должен быть непрозрачным для любого внешнего кода вызова. Я попытался бы найти другое решение, которое не зависит от иерархии наследования.

Ответ 2

Результаты в EXC_BAD_ACCESS:

[self retrieveItemsForId:idString completionHandler:^(NSError *error) {
 if (!error) {
  [super didRetrieveItems];
 } else {
  [super errorRetrievingItems];
 }
}];

Вероятно, из-за ошибки в компиляторе; попробуйте добавить [self class]; или любой другой вызов метода в себя в этом блоке, и это, вероятно, сработает.

Работает отлично, реализации -didRetrieveItems и -errorRetrievingItems находятся в суперклассе.

[self retrieveItemsForId:idString completionHandler:^(NSError *error) {

 if (!error) {
  [self didRetrieveItems];
 } else {
  [self errorRetrievingItems];
 }
}];

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

Из-за наследования ваш класс также эффективно реагирует на вызовы методов. Просто позвоните им, как вы делаете выше, используя self. Это будет работать, и именно так вы должны это делать!