Назначение себя в Objective-C - PullRequest
       22

Назначение себя в Objective-C

29 голосов
/ 27 августа 2009

Я из мира C ++, поэтому понятие присвоения this заставляет меня содрогнуться:

this = new Object; // Gah!

Но в Objective-C есть похожее ключевое слово, self, для которого это вполне приемлемо:

self = [super init]; // wait, what?

Во многих примерах кода Objective-C используется вышеприведенная строка в подпрограммах init. Мои вопросы:

1) Почему назначение на self имеет смысл (ответы типа «потому что язык это позволяет» не в счет)

2) Что произойдет, если я не назначу self в моей init процедуре? Я подвергаю свой экземпляр какой-то опасности?

3) Если следующее выражение if не выполнено, что это значит и что я должен сделать для его восстановления:

- (id) init
{
    self = [super init];

    if (self)
    {
        self.my_foo = 42;
    }

    return self;
}

Ответы [ 6 ]

33 голосов
/ 27 августа 2009

Это тема, которая часто оспаривается новичками:

По сути, это вытекает из идеи о том, что суперкласс мог переопределить назначенный инициализатор для возврата объекта, отличного от того, который был возвращен из +alloc. Если вы не присвоили возвращаемое значение инициализатора super в self, то вы могли бы иметь дело с частично инициализированным объектом (поскольку инициализированный super объект не является тем же объектом, что повторная инициализация).

В целом, super довольно редко возвращает что-то другое, но это происходит в нескольких случаях.

10 голосов
/ 27 августа 2009

В Objective-C инициализаторы имеют возможность вернуть nil в случае сбоя или вернуть совершенно другой объект, чем тот, который был вызван инициализатором (NSArray всегда делает это, например). Если вы не захватите возвращаемое значение init, метод может выполняться в контексте освобожденного объекта.

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

И да, это выглядит странно.

6 голосов
/ 29 августа 2009

Это правда, что init может вернуть nil, если инициализация не удалась. Но это не основная причина, по которой вы должны присваивать себе значение при реализации собственных инициализаторов.

Это было упомянуто ранее, но необходимо подчеркнуть еще больше: экземпляр, возвращенный из инициализатора, может не совпадать с экземпляром, который вы отправили, на самом деле он может даже не относиться к тому же классу!

Некоторые классы используют это как стандарт, например, все инициализаторы для NSString и NSArray всегда будут возвращать новый экземпляр другого класса. Инициализаторы к UIColor часто возвращают другой экземпляр специализированного класса.

И вы сами можете реализовать что-то подобное, если хотите:

-(id)initWithName:(NSString*)name;
{
  if ([name isEqualToString:@"Elvis"]) {
    [self release];
    self = [[TheKing alloc] init];
  } else if (self = [super init]){
    self.name = name;
  }
  return self;
}

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

3 голосов
/ 27 августа 2009

Все остальные пункты здесь верны, но для вас также важно понимать, что self является неявным параметром для каждого метода Objective-C (objc_msgSend() передает его) и может быть записан, как любой другой параметр метода. (Запись в явные параметры, как правило, не одобряется, если только они не являются параметрами.)

Как правило, это делается только методом -init по причинам, указанным другими. Это имеет какой-либо эффект, потому что self возвращается из метода и используется в присваивании id obj = [[NSObject alloc] init]; Это также влияет на неявное разрешение ivars, потому что, например, если myVar является ivar моего класса, то доступ к нему осуществляется в метод вызывает неявное разрешение до self->myVar.

1 голос
/ 28 августа 2009

Если [super init] возвращает nil, это означает, что вы были освобождены, а ваш параметр self теперь является недопустимым указателем. Слепо следуя соглашению self = [super init], вы избавите себя от потенциально неприятных ошибок.

Рассмотрим следующий нестандартный инициализатор:

- (id)initWithParam:(id)param {
    if (!param) {
        // Bad param.  Abort
        self = [super init]; // What if [super init] returns nil?
        [self release];
        return nil;
    }
    else 
    {
        // initialize with param.
        ...
    }
}

А что будет, если мой суперкласс решит прервать и вернуть ноль? Я был удален, и мой self параметр теперь недействителен, и [self release] завершится сбоем. Переназначая self, я избегаю этого сбоя.

1 голос
/ 27 августа 2009

Я все еще новичок в Objective C, но этот пост помог мне понять это.

Подводя итог, большинство вызовов init возвращают тот же объект, для которого self уже инициализирован. Если будет ошибка, init вернет ноль. Кроме того, некоторые объекты, такие как синглеты или уникальные объекты (например, NSNumber 0), будут возвращать объект, отличный от инициализированного (синглтон или глобальный объект 0). В этих ситуациях вам нужно самостоятельно ссылаться на этот объект. Я ни в коем случае не эксперт в том, что здесь происходит за кулисами, но для меня это имеет смысл на поверхности.

...