Я знаю, что уже немного поздно для моего ответа, но я не могу удержаться от публикации ссылки, которая показалась мне очень полезной для устранения моих сомнений по поводу этой проблемы.
Мэтт Галлахер: Что это значит, когда вы назначаете [super init] для себя?
РЕДАКТИРОВАТЬ: В соответствии с комментариями, вот основные пункты в ссылке
Чтобы понять, почему self=[super init];
нам нужно учесть много моментов. Давайте займемся этим один за другим.
Что такое self
Каждый метод имеет два скрытых параметра: self
и _cmd
. Так что вызов метода
- (id)initWithString:(NSString *)aString
заменяется компилятором на вызов функции, подобный этому
id initWithString(id self, SEL _cmd, NSString *aString);
Зачем нам нужно я?
Реальность такова, что компилятор использует параметр self
для разрешения любой ссылки на переменную экземпляра внутри метода.
Предположим, что у нас есть метод setValueToZero
и value
- переменная экземпляра класса, к которому он принадлежит, тогда реализация
- (void)setValueToZero
{
value = 0;
}
будет преобразован компилятором в функцию, подобную этой
void setValueToZero(id self, SEL _cmd)
{
self->value = 0;
}
У self
уже есть значение при вызове init
?
Ниже приведен пример типичного создания и инициализации объекта.
[[MyClass alloc] initWithString:@"someString"]
Здесь, к тому времени, когда мы перейдем к методу initWithString
, self получит в качестве значения вновь выделенный объект (т.е. возвращаемое значение из [MyClass alloc]
). Фактически, это почти гарантированно будет правильным, окончательным значением.
Почему self = [super init];
?
Это потому, что [super init]
разрешено делать одно из трех:
- Вернуть свой собственный получатель (указатель
self
не изменяется) с инициализированными значениями унаследованных экземпляров.
- Возвращает другой объект с инициализированными унаследованными значениями экземпляра.
- Возврат
nil
, что указывает на ошибку.
В первом случае назначение не влияет на self
. В третьем случае инициализация не удалась, self
установлен на nil
и возвращается.
Причина присвоения self
связана со вторым случаем. Рассмотрим следующее
- (id)initWithString:(NSString *)aString
{
self = [super init];
if (self)
{
instanceString = [aString retain];
}
return self;
}
Мы хотим преобразование из
instanceString = [aString retain];
до
self->instanceString = [aString retain];
, чтобы действовать на правильное значение, и поэтому мы должны изменить значение self
.
Когда [super init]
вернет другой объект?
В одной из следующих ситуаций
- Синглтон-объект (всегда возвращает синглтон вместо любого последующего выделения)
- Другие уникальные объекты (
[NSNumber numberWithInteger:0]
всегда возвращает глобальный «нулевой» объект)
- Кластеры классов заменяют частные подклассы при инициализации экземпляра суперкласса.
- Классы, которые решили перераспределить один и тот же (или совместимый) класс на основе параметров, переданных в инициализатор.
Во всех случаях, кроме последнего, продолжение инициализации возвращаемого объекта, если он изменяется, является ошибкой - возвращенный объект уже полностью инициализирован и больше не нужен для вашего класса. Таким образом, лучший подход к инициализации будет следующим:
- (id)initWithString:(NSString *)aString
{
id result = [super init];
if (self == result)
{
instanceString = [aString retain];
}
return result;
}
Заключение
Вам не нужно присваивать [super init]
self
, чтобы большинство классов работало. В некоторых неясных случаях это на самом деле неправильно.
Так почему же мы продолжаем присваивать self
? Это традиционный шаблон для инициализатора, и, хотя в некоторых случаях он неправильный, в других случаях, которые были написаны, ожидать такого подхода.