У меня есть общий вопрос о написании методов инициализации в Objective-C.
Я вижу везде (код Apple, книги, открытый исходный код и т. Д.), Что метод init должен проверить, не равен ли self = [super init] ноль, прежде чем продолжить инициализацию.
Шаблон Apple по умолчанию для метода init:
- (id) init
{
self = [super init];
if (self != nil)
{
// your code here
}
return self;
}
Зачем?
Я имею в виду, когда init когда-нибудь вернет ноль? Если я вызвал init в NSObject и получил nil обратно, то что-то должно быть действительно испорчено, верно? И в этом случае вы могли бы даже не написать программу ...
Действительно ли так часто, что метод init класса может возвращать ноль? Если да, то в каком случае и почему?
objective-c
null
init
Jasarien
источник
источник
Ответы:
Например:
... и так далее. (Примечание: NSData может выдать исключение, если файл не существует). Существует довольно много областей, в которых возвращение nil - это ожидаемое поведение при возникновении проблемы, и из-за этого стандартная практика заключается в том, чтобы проверять nil почти все время, для обеспечения согласованности.
источник
Эта идиома является стандартной, потому что она работает во всех случаях.
Хотя необычно, будут случаи, когда ...
... возвращает другой экземпляр, поэтому требует присваивания себе.
И будут случаи, когда он вернет nil, поэтому потребуется проверка nil, чтобы ваш код не пытался инициализировать слот переменной экземпляра, который больше не существует.
Суть в том, что это задокументированный правильный шаблон, и если вы его не используете, вы делаете это неправильно.
источник
-init
NSObject, кажется, не имеет ничего для своего афикта ...)[super init]
возвращается ноль, когда прямой суперклассNSObject
? Разве это не тот случай, когда "все сломано?"NSObject
это прямой суперкласс. Но ... даже если вы объявитеNSObject
прямой суперкласс, что-то могло быть изменено во время выполнения так, чтоNSObject
его реализацияinit
не то, что на самом деле называется.Я думаю, что в большинстве классов, если возвращаемое значение из [super init] равно nil, и вы проверяете его, как рекомендовано стандартными методами, а затем возвращаете преждевременно, если nil, в основном ваше приложение все еще не будет работать правильно. Если вы думаете об этом, несмотря на то, что если (само! = Ноль) проверка есть, для правильной работы вашего класса, 99,99% времени вы на самом деле делаете потребность себя , чтобы быть не равна нулю. Теперь, предположим, по какой-то причине, [super init] действительно возвратил nil, в основном, ваша проверка против nil в основном передает баг до вызывающей стороны вашего класса, где он, вероятно, в любом случае потерпит неудачу, поскольку он, естественно, предположит, что вызов был успешный.
По сути, я нахожусь в том, что в 99,99% случаев if (self! = Nil) ничего не покупает с точки зрения большей надежности, поскольку вы просто перекладываете деньги на своего призывателя. Чтобы по-настоящему справиться с этим, вам нужно будет проверять всю иерархию вызовов. И даже в этом случае единственное, что он может купить, - это то, что ваше приложение не сможет работать более чисто / надежно. Но все равно не получится.
Если библиотечный класс произвольно решил вернуть nil в результате [super init], вы в любом случае в значительной степени ошеломлены, и это скорее указывает на то, что автор библиотечного класса допустил ошибку реализации.
Я думаю, что это скорее предложение устаревшего кодирования, когда приложения запускались в гораздо более ограниченной памяти.
Но для кода уровня C я все равно обычно проверял бы возвращаемое значение malloc () по нулевому указателю. Принимая во внимание, что для Objective-C до тех пор, пока я не найду доказательства обратного, я думаю, что обычно я пропускаю проверки if (self! = Nil). Почему расхождение?
Потому что на уровнях C и malloc в некоторых случаях вы можете частично восстановиться. В то время как я думаю, что в Objective-C, в 99,99% случаев, если [super init] действительно возвращает nil, вы в сущности офигены, даже если вы пытаетесь справиться с этим. Вы могли бы просто позволить приложению аварийно завершить работу и справиться с последствиями.
источник
Это своего рода краткое изложение комментариев выше.
Допустим, суперкласс возвращается
nil
. Что будет дальше?Если вы не следуете конвенциям
Ваш код потерпит крах в середине вашего
init
метода. (еслиinit
не делает ничего значимого)Если вы будете следовать соглашениям, не зная, что суперкласс может вернуть ноль (большинство людей в конечном итоге здесь)
Ваш код, вероятно, потерпит крах в какой-то момент позже, потому что ваш экземпляр находится там
nil
, где вы ожидали чего-то другого. Или ваша программа будет работать неожиданно без сбоев. О, Боже! Вы хотите, чтобы это? Я не знаю...Если вы будете следовать соглашениям, добровольно разрешив вашему подклассу вернуть ноль
Ваша документация по коду (!) Должна четко указывать: «возвращает ... или ноль», а остальная часть вашего кода должна быть подготовлена для обработки этого. Теперь это имеет смысл.
источник
Как правило, если ваш класс является производным от
NSObject
вас, вам это не нужно. Тем не менее, это хорошая привычка, как если бы ваш класс был производным от других классов, их инициализаторы могут возвращатьсяnil
, и если это так, ваш инициализатор может затем захватить это и вести себя правильно.И да, для протокола: я следую лучшей практике и пишу ее на всех своих уроках, даже на тех, которые происходят непосредственно от них
NSObject
.источник
Foo *bar = [[Foo alloc] init]; if (bar) {[bar doStuff];}
NSObject
не гарантирует, что оно-init
дает и вамNSObject
, если учесть экзотические среды выполнения, такие как более ранние версии GNUstep в (которые возвращаютсяGSObject
), независимо от того, что, чек и присвоение.Вы правы, вы часто можете просто написать
[super init]
, но это не сработает для подкласса всего. Люди предпочитают просто запомнить одну стандартную строку кода и использовать ее все время, даже когда это только иногда необходимо, и, таким образом, мы получаем стандартif (self = [super init])
, который использует как возможность возврата nil, так и возможность объекта, отличного отself
возврата в учетную запись.источник
Распространенная ошибка - написать
который возвращает экземпляр суперкласса, который НЕ является тем, что вы хотите в конструкторе / init подкласса. Вы возвращаете объект, который не отвечает на методы подкласса, что может сбивать с толку, и генерировать ошибочные ошибки, связанные с невозможностью ответить на методы или идентификаторы, которые не найдены, и т. Д.
необходим, если у суперкласса есть члены (переменные или другие объекты), чтобы сначала инициализировать перед настройкой членов подклассов. В противном случае среда выполнения objc инициализирует их все в 0 или в ноль . (в отличие от ANSI C, который часто выделяет куски памяти, не очищая их вообще )
И да, инициализация базового класса может произойти сбой из-за ошибок нехватки памяти, отсутствующих компонентов, сбоев получения ресурсов и т. Д., Поэтому проверка на ноль является разумной и занимает менее нескольких миллисекунд.
источник
Это делается для проверки того, что инициализация сработала, оператор if возвращает true, если метод init не возвратил nil, поэтому это способ проверить создание объекта, сработавшего корректно. Несколько причин, по которым я могу думать о том, что init может потерпеть неудачу, может быть, это переопределенный метод init, о котором суперкласс не знает, или что-то в этом роде, хотя я не думаю, что это так часто встречается. Но если это случится, лучше ничего не случиться, если я получу аварию, поэтому всегда проверяю ...
источник
В OS X это не так вероятно для
-[NSObject init]
сбоя по причинам памяти. Этого нельзя сказать о iOS.Кроме того, рекомендуется писать при создании подкласса класса, который может вернуться
nil
по любой причине.источник
-[NSObject init]
является очень вряд ли удастся из - за соображения памяти , как он не выделяет память.