IOS: подкласс UIView init или initWithFrame:?
Я создал подкласс UIView
, который имеет фиксированный фрейм. Итак, могу ли я просто переопределить init
вместо initWithFrame:
? Например:.
- (id)init {
if ((self = [super initWithFrame:[[UIScreen mainScreen] bounds]])) {
self.backgroundColor = [UIColor clearColor];
}
return self;
}
В документации Xcode для -initWithFrame:
говорится: "Если вы создаете объект представления программно, этот метод является назначенным инициализатором для класса UIView
. Подклассы могут переопределять этот метод для выполнения любой пользовательской инициализации, но должны вызывать super
в начале их реализации".
Что означает "назначенный инициализатор"?
Ответы
Ответ 1
Назначенный инициализатор - это тот, который должны вызвать все остальные инициализаторы. UIView
и подклассы немного необычны в том, что у них фактически есть два таких инициализатора: -initWithFrame:
и -initWithCoder:
, в зависимости от того, как создается представление. Вы должны переопределить -initWithFrame:
, если вы создаете представление в коде, и -initWithCoder:
, если вы загружаете его из наконечника. Или вы можете поместить свой код в третий метод и переопределить оба эти инициализатора таким образом, чтобы они вызывали ваш третий метод. Фактически, это часто рекомендуемая стратегия.
Итак, например, вы можете создать подкласс UIView, ClueCharacter
, который имеет свой собственный метод инициализации: -initWithPerson:place:thing:
. Затем вы создадите свое представление следующим образом:
Obj-C:
ClueCharacter *mustard = [[ClueCharacter alloc] initWithPerson:@"Col. Mustard"
place:kInTheStudy
thing:kTheRope];
Swift:
var mustard = ClueCharacter("Col. Mustard", place: kInTheStudy, thing: kTheRope)
Это хорошо, но для инициализации части UIView объекта ваш метод должен вызывать назначенный инициализатор:
Obj-C:
-(id)initWithPerson:(NSString*)name place:(CluePlace)place thing:(ClueWeapon)thing
{
if ((self = [super initWithFrame:CGRectMake(0, 0, 150, 200)])) {
// your init stuff here
}
}
Swift:
func init(name: String, place : CluePlace, thing : ClueWeapon)
{
if (self = super.init(CGRectMake(0, 0, 150, 200))) {
// your init stuff here
}
}
Если вы хотите вызвать свой инициализатор подкласса -init
, это нормально, если вы вызываете -initWithFrame:
в реализации.
Ответ 2
в UIView
вызов [super init]
в точности равен [super initWithFrame:CGRectZero]
Ответ 3
В классе Objective-C с несколькими инициализаторами назначенный инициализатор является тем, который выполняет значимую работу. Итак, часто у вас есть класс с несколькими инициализаторами, скажем:
- (id)initWithRect:(CGRect)someRect;
- (id)initWithRect:(CGRect)someRect setDefaultColour:(BOOL)setDefaultColour;
- (id)initWithRect:(CGRect)someRect setDefaultColour:(BOOL)setDefaultColour
linkTo:(id)someOtherObject;
В этом случае вы обычно (но не всегда) говорите, что третий был назначен инициализатором и реализовал другие два, например,
- (id)initWithRect:(CGRect)someRect
{
return [self initWithRect:someRect setDefaultColour:NO];
}
- (id)initWithRect:(CGRect)someRect setDefaultColour:(BOOL)setDefaultColour
{
return [self initWithRect:someRect setDefaultColour:setDefaultColour
linkTo:nil];
}
Если класс имеет только один инициализатор, тогда назначенный инициализатор.
В вашем случае, чтобы следовать лучшей практике, вы должны реализовать initWithFrame:
, а также vanilla init:
, который вызывает initWithFrame:
с вашими нормальными размерами. Обычное соглашение состоит в том, что вы можете добавлять новые изменения в init
в подклассах, но не должны убираться, и что вы всегда выполняете фактическую инициализацию в назначенном инициализаторе. Это позволяет любым инициализирующим методам из родительского класса, что вы не предоставляете новые реализации, которые все еще должны работать с вашим подклассом.