2013-05-31 3 views
2

Предположим, у вас есть подкласс UIView. Вы определяете метод init «myInitWithFrame: ... andWhatNot: ...». Вы знаете, что вы не будете использовать метод init, унаследованный от UIView , когда-либо, и ваш пользовательский метод инициализации выполняет некоторые важные пользовательские инициализации, поэтому вы хотите заставить клиентские классы никогда не использовать унаследованный метод initWithFrame.Наследование: ограничение, а не расширение?

Можно ли скрыть стандартный метод initWithFrame, который был унаследован от UIView?

+1

Извините, но как это дублируется? –

ответ

19

На самом деле, вы можете получать предупреждения времени компиляции о вызове метода на подклассе. Используйте атрибут __attribute((deprecated)). Если вы хотите, чтобы люди использовать -initWithPizza: вместо -initWithFrame:, сделайте следующее:

@interface MyView : UIView 
- (id)initWithPizza:(MyPizza *)pizza; 
@end 

@interface MyView (Deprecations) 
- (id)initWithFrame:(CGRect)frame __attribute((deprecated("Use initWithPizza: instead"))); 
@end 

Ввод -initWithFrame: декларации в качестве отдельной категории необходимо избегать Xcode жаловаться, что вы объявили метод в заголовке, но не реализовали его. Поскольку вы просто наследуете его от суперкласса, все в порядке; у вас нет есть, чтобы реализовать его вообще. Но если вы хотите реализовать его, чтобы выдать исключение, или перейдите к -initWithPizza: с аргументом по умолчанию, это нормально.

Конечно, это не остановит UIKit от звонка -initWithFrame:, если он уже собирался это сделать. Но если вы можете гарантировать, что этого не произойдет, тогда вы в порядке.

+0

Спасибо! Особенно для публикации после того, как я уже принял другой ответ. Отлично! –

+0

Если я попытаюсь поместить эти две вещи в заголовочный файл моего представления, он не будет компилироваться. Он утверждает, что атрибут имеет неправильное количество аргументов. Я могу «исправить» его и скомпилировать с помощью: - (id) initWithFrame: (CGRect) frame __attribute __ ((устарело)); вместо этого. Затем он компилируется, но без каких-либо предупреждений, хотя я вызываю этот метод где-то еще, чтобы поднять предупреждение компилятора. –

+0

Hm. Используете ли вы последнюю версию Xcode? Я использую Xcode 4.6.2, и код, который я предоставил, компилируется и предоставляет предупреждения, когда я его вызываю. –

3

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

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

Например, UIKit не знает вашего подкласса. Поэтому, если вы хотите сделать свой подкласс UIView доступным из NIB, вам необходимо использовать методы инициализации, которые будут вызываться системой загрузки NIB, а именно initWithCoder:. Вы можете просто вызвать свои собственные методы инициализации внутри initWithCoder:. Но если есть какие-либо дополнительные параметры, которые вы хотели бы передать методу init, вам придется предоставить способ их настройки после инициализации.

+0

Справа. У меня есть параметры, которые я передаю, поэтому я не могу вызвать свой собственный метод из стандартного метода. Дух, какая дилемма. –

+1

Вот обсуждение этой темы в большом проекте с открытым исходным кодом - AFNetworking недавно решила исключить исключение в 'init': https://github.com/AFNetworking/AFNetworking/pull/1019#issuecomment-18312495 –

+0

@AaronBrager Cheers ! –

7

На самом деле, вы можете ограничить использование подкласса. Вы можете переопределить любые методы, которые вы хотите заблокировать в файле .h вашего подкласса. Вы можете сделать initWithFrame недоступным, разместив в своем .h файле.

- (id) initWithFrame:(CGRect) frame __attribute__((unavailable("message"))); 

Это делает метод initWithFrame: недоступным для любого, используя свой подкласс.

Чтобы сохранить другую форму кода, вызывающую этот метод, вы можете дополнительно ограничить это, разместив это в своем файле .m.

- (id) initWithFrame:(CGRect) frame 
{ 
    return nil;  
} 
+0

Это, похоже, не компилируется. Но если я удалю аргумент, он это сделает. Добавление второго предложения в класс дает предупреждение компилятора. «Атрибут метода может быть указан только в объявлении метода». Но даже так: я нахожу ваш ответ довольно полезным и не имею ничего плохого в том, что вы выбиваете! –

+1

Возможно, вы могли бы отредактировать свой ответ и изменить первый бит кода, чтобы скомпилировать? –

+0

Не добавляйте атрибут в реализацию или полностью опустите реализацию. – Jano

Смежные вопросы