Приводит ли новый компилятор LLVM в Xcode4 к неправильной работе наследования классов?

#iphone #objective-c #xcode #xcode4 #llvm

#iPhone #objective-c #xcode #xcode4 #llvm

Вопрос:

РЕДАКТИРОВАТЬ: проблема решена! После очистки и перезагрузки он просто исчез! Я не знаю, что вызвало это!

Это вызвало у меня головную боль на целый день:

В Xcode 3.2 все работало отлично. Затем я переключился на 4.2, и внезапно наследование классов больше не работает.

У меня есть класс TheSuperclass и TheSubclass : TheSuperclass . Чтобы упростить тестирование, я действительно создал их такими. Кода не больше, чем вы можете увидеть здесь:

 // TheSuperclass.h
@interface TheSuperclass : NSObject {

}

// subclasses must override this method
- (id)returnSomethingUseful;

@end
  

 // TheSuperclass.m
#import "TheSuperclass.h"

@implementation TheSuperclass
- (id)returnSomethingUseful {
    NSLog(@"Dude, you have to override -returnSomethingUseful");
    return nil; // subclasses override this method!
}


- (id)init {
    if ((self = [super init])) {
            obj = [self returnSomethingUseful]; // TEST
        NSLog(@"TheSuperclass initialized: %@", obj);
    }
    return self;
}
@end
  

 // TheSubclass.h
#import "TheSuperclass.h"

@interface TheSubclass : TheSuperclass {

}

@end
  

 // TheSubclass.h
#import "TheSubclass.h"

- (id)returnSomethingUseful {
    NSLog(@"Correct method called!");
    return usefulObject;
}
  

 TheSubclass *foo = [[TheSubclass alloc] init]; // remember: init of superclass calls the method
// Getting the NSLog: "Dude, you have to override -returnSomethingUseful"


id bar = [foo returnSomethingUseful]; // now call it directly on foo
// Getting the NSLog: "Correct method called!"
  

TheSuperclass объявляет метод шаблона, то есть метод, который просто ничего не делает, возвращает nil и предназначен для создания подклассов:

 - (id)returnSomethingUseful {
    NSLog(@"Dude, you have to override -returnSomethingUseful");
    return nil; // subclasses override this method!
}
  

В TheSubclass я просто переопределяю этот метод шаблона. И, конечно, я СКОПИРОВАЛ реализацию, TheSuperclass чтобы получить ее на 100% правильно. Опечатки нет. Это проверенный факт. Выглядит так:

 - (id)returnSomethingUseful {
    NSLog(@"Correct method called!");
    return usefulObject;
}
  

При реализации TheSuperclass фрагмента кода вызывается [self returnSomethingUseful] , чтобы получить этот объект из метода шаблона. Это отличный шаблон, и я часто его использовал. Это всегда работало именно так.

Но теперь кажется, что, хотя я создаю экземпляр TheSubclass , он всегда вызывает НЕПРАВИЛЬНЫЙ метод. Тот, который из суперкласса, вместо того, который он должен (который, конечно, является методом переопределения)

Я проверял это по крайней мере 100 раз! Серьезно, это экземпляр TheSubclass . Он вызывает метод init TheSuperclass для предоставления мне NSLogs.

Теперь действительно странная часть: когда я вызываю этот метод для моего объекта subclass «извне», он работает:

 TheSubclass *foo = [[TheSubclass alloc] init];
id bar = [foo returnSomethingUseful];
// Getting the NSLog: "Correct method called!"
  

Итак, чтобы подчеркнуть это: когда я вызываю [self returnSomethingUseful] из реализации суперкласса, он вызывает НЕПРАВИЛЬНУЮ реализацию (которая является реализацией суперкласса, а не перезаписанной в подклассе.

Итак, как я могу обойти это? Откуда это может взяться? Это проблема во время выполнения, возможно, вызванная ошибкой в компиляторе?

Комментарии:

1. Может быть, очистить и перестроить? Возможно, у компилятора проблемы с обновлением. Также рассмотрите возможность использования точек останова вместо NSLogs.

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

3. «это вызывает НЕПРАВИЛЬНУЮ реализацию» Именно поэтому вы должны показать свою реализацию [TheSubclass init] . Похоже, что все, что вы возвращаете, на самом деле не является экземпляром subclass .

4. Обновил мой вопрос. Теперь отображается весь код. Для упрощения тестирования нет метода инициализации. Я наследую один из суперклассов.

Ответ №1:

LLVM явно способен правильно реализовать простое разделение на подклассы. Тысячи программ используют его каждый день, так что это не проблема, которую нужно обойти, если вы пишете стандартный код (не пробуя ничего сложного или недокументированного). Вот что вам следует сделать:

  • Перезагрузка. Xcode делает вид, что вам не нужно перезагружаться после установки. По моему опыту, вы это делаете. Он решает 90% проблем после обновления Xcode.
  • Очистите и перестройте. Это с меньшей вероятностью исправит проблему, потому что переход с 3.2 на 4.2 в любом случае помещает производные файлы в совершенно разные места. Но попробовать стоит.
  • Убедитесь, [self class] что вы действительно тот, кем вы себя считаете.
  • Убедитесь, что вы никогда не путаетесь с isa указателем (легко найти; поиск ->isa в вашем коде).
  • Проверьте свой init . Это наиболее вероятное место, где можно было сделать что-то слишком сложное. Убедитесь, что вы следуете простым шаблонам.
  • Выведите простейшую форму проблемы и опубликуйте некоторый код, который ее демонстрирует. Из вашего описания это должно быть возможно сделать в очень немногих строках кода. Попытка создать эту упрощенную версию чаще всего покажет вам, в чем заключается ваша ошибка.

Комментарии:

1. Большое спасибо, Роб. Я попробую это сделать. Что касается причудливых деталей: все, что я сделал, приведено выше в моем обновленном вопросе. Никаких методов, никаких трюков.

2. После очистки, перезагрузки и повторной сборки проблема исчезла!

Ответ №2:

Если компилятор испортил что-то настолько простое, система не загрузилась бы.

Подтвердите класс в вашем init методе, потому что либо у вас есть экземпляр суперкласса, либо что-то написано с ошибкой.

Ответ №3:

Термин «переопределить», а не «перезаписать».

Вы не показали нам достаточно своего кода, чтобы понять, в чем проблема. Покажите нам методы -init для обоих классов и покажите нам, где создается ваш экземпляр подкласса.

Комментарии:

1. кстати, спасибо за исправление формулировки. Я всегда путаю переопределение с перезаписью. Не является носителем английского языка.