почему инициализация подклассов требует вызова той же функции инициализации суперкласса? - PullRequest
2 голосов
/ 23 октября 2010

Я слышал, что когда у вас есть подкласс, вы должны инициализировать суперкласс той же функцией init из инициализации подкласса.Я имею в виду, что init подкласса должен вызывать [super init], а initWithFrame подкласса должен вызывать [super initWithFrame].Почему это?Почему вызов init из суперкласса из initWithFrame подкласса приводит к бесконечному циклу?

Если это требуется, то означает ли это, что я не могу создать новую функцию init в подклассе, например initWithPoint, и у меня этот вызовSuper's init или initWithFrame просто потому, что у суперкласса нет initWithPoint?Я предполагаю, что суть вопроса здесь просто в том, почему неуместно называть другой суперкласс, что меня смущает, возможно, из-за моего опыта в c ++?

Ответы [ 5 ]

3 голосов
/ 23 октября 2010

Почему вызов инициатора супер-объекта из initWithFrame подкласса приводит к бесконечному циклу?

Исходя из фона C ++, как вы говорите, основная проблема, вероятно, заключается в том, что выпривык к парадигме вызова метода C ++.В Objective-C вы не вызываете функции объектов.Даже технически не совсем правильно говорить, что вы вызываете методы.В Objective-C вы отправляете сообщения объектам, и объект решает, что с ними делать.Обычно он ищет метод в своем классе и вызывает его.Следствием этого является то, что вы не можете контролировать, какая версия метода в иерархии классов вызывается сообщением.Это всегда метод, принадлежащий к классу объекта, которому вы отправляете сообщение (кроме одного случая).Как будто в C ++ нет не виртуальных функций, даже конструкторов.

Единственное исключение - это когда вы отправляете сообщение super.В этом случае метод вашего класса обойден.Это может привести к бесконечным циклам, как вы узнали.Причина в том, что мы не вызываем функции, мы отправляем сообщения.Поэтому, если methodA в классе SubKlass отправляет [super methodB], будет вызвана реализация methodB в Klass.Если затем он отправляет [self methodA], то self остается экземпляром SubKlass, он волшебным образом не преобразуется в экземпляр Klass, поэтому будет вызываться метод A в SubKlass.

Именно поэтому правила для инициализаторов выглядят такзапутанный.Только назначенный инициализатор гарантированно не отправит один из других инициализаторов, поэтому вы можете безопасно отправлять только назначенный инициализатор super в свой инициализатор.

3 голосов
/ 23 октября 2010

Почему это?Почему вызов init из суперкласса из initWithFrame подкласса приводит к бесконечному циклу?

Если -init в супер реализовано как

-(id)init {
  return [self initWithFrame:CGRectZero];
}

, тогда граф вызовов будет зацикленвокруг:

[subclass initWithFrame:]
   |     ^
   v     |
[super init]

, поскольку self всегда использует текущий класс ("подкласс").


Если это требуется, то означает ли это, что я могу 't создать новую функцию init в подклассе, таком как initWithPoint, и иметь этот вызов супер-init или initWithFrame просто потому, что суперкласс не имеет initWithPoint?

Нет, это не требуется.Что предпочтительнее, так это вызывать наиболее специализированный инициализатор super, так что нет никаких шансов, что супер -initXXX перезвонит подклассу -initYYY.

3 голосов
/ 23 октября 2010

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

В рамках инициализации вашего подкласса вы должны вызвать один из назначенных инициализаторов суперкласса.

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

Подробнее см. «Язык программирования Objective-C: выделение и инициализация объектов». [Примечание: по состоянию на декабрь 2013 г. этот контент больше не доступен через документ Apple центр. То, что было языковым справочником, было заменено более ориентированными на задачи учебниками и концептуальной документацией.]

Что касается ваших конкретных вопросов:

Почему это так? Так что у суперкласса есть шанс инициализировать свое состояние. Затем вы можете продолжить и инициализировать состояние, которое вы добавляете, сверх того, что обеспечивает суперкласс.

Почему вызов init супервизора из initWithFrame подкласса приводит к бесконечному циклу? Поскольку для NSView, -init не является назначенным инициализатором, хотя это NSObject. Таким образом, NSView переопределяет его для вызова назначенного инициализатора, -initWithFrame:. Если вы звонили -init с вашего -initWithFrame:, теперь у вас есть -initWithFrame: звонок -init звонок -initWithFrame: звонок -init: звонок ...

Означает ли это ...? Нет, потому что это не обязательно. Вы должны понимать фактическую документацию, а не слухи.

1 голос
/ 23 октября 2010

с точки зрения c ++:

Я слышал, что когда у вас есть подкласс, вы должны инициализировать суперкласс той же функцией init из инициализации подкласса.Я имею в виду, что init подкласса должен вызывать [super init], а initWithFrame подкласса должен вызывать [super initWithFrame].

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

это может помочь просмотреть его так: посмотрите на инициализаторы суперкласса и определите, какие из них поддерживаются.

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

для обозначенных инициализаторов: считайте его защищенным

для нового инициализатора: считайте его защищенным

для унаследованных инициализаторов: обычно считайте закрытым, когдасуперкласс объявляет новые инициализаторы, иначе защищенные

Почему вызов init суперкадра из initWithFrame подкласса приводит к бесконечному циклу?

таков эффект (неопределенное поведение)вызов интилизатора, который вы не должны вызывать.

Если это необходимо, тоОзначает ли это, что я не могу создать новую функцию init внутри подкласса, такого как initWithPoint, и иметь этот вызов super init или initWithFrame просто потому, что у суперкласса нет initWithPoint?

это хорошо, какПока вы звоните через один из поддерживаемых инициализаторов суперкласса.

Я думаю, суть вопроса здесь просто в том, почему неправильно вызывать другой суперкласс, что меня смущает, возможно, из-за моего c ++background?

objc не поддерживает скрытие / видимость для инициализаторов.как только он находится в интерфейсе суперкласса, он там (и вы можете сделать неправильный выбор, когда компилятор не может вам помочь) - вы должны определить граф видимости для инициализаторов и написать свой подкласс соответственно.В objc отсутствуют языковые функции, которые вы привыкли иметь в c ++.

0 голосов
/ 23 октября 2010

Я не знаю, откуда вы это услышали, но AFAIK это не требуется. Вы можете выбрать инициализацию вашего подкласса с тем, что вы хотите, при условии, что вы вызываете метод init суперкласса. Любой метод инициализации будет работать.

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

...