Зачем создавать пользовательские init, когда ivars / properties могут быть установлены вне экземпляра? - PullRequest
1 голос
/ 25 октября 2011

Я читаю книгу Objective-C, и у меня есть вопрос, на который книга, похоже, не отвечает.

Допустим, у меня есть два пользовательских класса.

Первый класс называется ClassA.Конечно, у него есть файлы .h и .m.Второй класс называется ClassB.Он также имеет файлы .h и .m.

Где-то в коде у ClassA есть такой метод:

-(IBAction)displaySomeText:(id)sender {
    ClassB *myNumber = [[ClassB  alloc]init];

    NSString *numberString = [myNumber storedNumberAsString];
    // storedNumberAsString is just a method that returns a string object that holds
    // myVariable.

    [textView insertText:numberString];
    //textView is a object  I created that just displays some text on screen.

    [myNumber release];
}

В книге сказано, что ClassB должен иметь метод:

-(id)init {
    [super init]; //I know why this is done, the book explains it well.
    myVariable = 42; // I created this variable already in the ClassB .h file 
    return self;
}

Теперь, когда в Интерфейсном Разработчике я нажимаю подключенные кнопки и т. Д. Это работает, отображается число 42.

У меня вопрос, почему янеобходимо создать метод -(id)init для ClassB, если я могу сделать следующее в методе ClassA:

-(IBAction)displaySomeText:(id)sender {
    ClassB *myNumber = [[ClassB alloc]init];
    myNumber.myVariable = 42; //I just do this to skip the -(id)init method.

    NSString *numberString = [myNumber storedNumberAsString];
    [textView insertText:numberString];
    [myNumber release]; 
}

При этом все равно будет отображаться то же значение: 42.Я могу изменить это на то, что мне нравится.Так почему бы просто не использовать init, унаследованный от NSObject, и просто сделать простой способ myNumber.myVariable = 42?

Ответы [ 3 ]

3 голосов
/ 25 октября 2011

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

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

В некоторых случаях значение ивара может быть частьюинформация, которая известна только ClassA или должна быть рассчитана на основе такой информации.Затем вы должны создать пользовательский инициализатор для ClassB, который получает это значение, например, - (id) initWithInteger:. Это станет «назначенным инициализатором» , и вы затем переопределите -[ClassB init], чтобы вызвать его с некоторым разумнымзначение по умолчанию.

1 голос
/ 25 октября 2011

Это довольно сложная проблема. Меня «воспитали» мысль, что после запуска конструктора (инициализатора) объект должен быть готов к работе. Вы должны быть в состоянии безопасно вызвать любой метод на нем. Следовательно, вам нужно установить любые переменные экземпляра в конструкторе, для которых 0 не является допустимым значением. Мне нравится их настраивать, если они все равно имеют 0 значений, просто для здравого смысла, потому что я никогда не хочу беспокоиться о том, чтобы знать мелкие детали каждого языка, с которым я работаю, например, будут ли они инициализировать переменные экземпляра равными 0.

Однако есть некоторые аргументы для того, чтобы не инициализировать некоторые переменные.

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

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

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

1 голос
/ 25 октября 2011

Если экземпляры ClassB не должны иметь ничего инициализированного (кроме нуля / нуля), вам не нужно создавать явный метод init для ClassB.В этом случае вопрос заключается в том, является ли установка myVariable в 42 ответом ClassB на жизнь, вселенную и все , или же myVariable - это просто поле в ClassB, которое может быть установлено на любоезначение.

То есть проблема носит концептуальный, а не физический смысл.Если концептуально значение 42 «принадлежит» ClassB, то для ClassB должен быть метод init, который его устанавливает.Если это конкретное значение имеет большее значение для ClassA, чем для ClassB, то какой-то метод ClassA должен установить его.Если вы сделаете это «неправильно», код все равно будет работать нормально, но ваш дизайн будет несколько менее элегантным, чуть менее расширяемым, чуть менее надежным.

...