Понимание себя в Objective-C - PullRequest
5 голосов
/ 15 февраля 2012

Код ниже взят из курса iTunes U для iPhone-разработчика в Objective-C. Я прочитал документацию Apple, и все это очень и очень ясно, за исключением себя. Я как бы понимаю себя, чтобы быть указателем на себя, но что именно это означает? В приведенном ниже коде, что именно означает сам? В чем разница между self.topSpeed ​​и self.nearestWormhole в файле реализации или self ссылается на одну и ту же вещь в обоих случаях? Относится ли self.topSpeed ​​к Планете *, а self.nearestWormhole - к Червоточине *? Спасибо всем, кто отвечает, я выучил C и теперь пытаюсь выучить ООП, поэтому любые отзывы приветствуются.

(Header file)
#import "Vehicle.h"
#import "Planet.h"
@interface Spaceship : Vehicle
@property (nonatomic) double topSpeed;
- (void)orbitPlanet:(Planet *)aPlanet
         atAltitude:(double)km;
@end





(Implementation file)
#import "Spaceship.h"
@interface Spaceship()
@property (nonatomic, strong) Wormhole *nearestWormhole;
@end

@implementation Spaceship
@synthesize topSpeed = _topSpeed;
@synthesize nearestWormhole = _nearestWormhole;

- (void)setTopSpeed:(double)speed
{
    if ((speed < 1) && (speed > 0)) _topSpeed = speed;
}

- (void)orbitPlanet:(Planet *)aPlanet atAltitude:(double)km
{
    double speed = self.topSpeed;
    if (speed > MAX_RELATIVE) speed = MAX_RELATIVE;
    [self.nearestWormhole travelToPlanet:aPlanet
                                 atSpeed:speed];
}
@end

Ответы [ 5 ]

4 голосов
/ 15 февраля 2012

self (или this в C ++) относится к объекту, выполняющему метод (или «для которого вызывается метод»).

Предположим, у меня есть комната с тремя людьми, Артуром, Бетти и Зигги, и коробкой шляп.Мы также определяем, что

Учитель Артура - Бетти.

Учительница Бетти - Зигги.

У Зигги нет учителя.

Я хочу дать следующий набор инструкций всем трем людям:

1.Наденьте шляпу на голову Зигги.

Это довольно легко.«Зигги» означает одно и то же лицо для Артура, Бетти и даже Зигги.Независимо от того, кто следует этой инструкции, тот же человек получает шляпу.

2.Наденьте шапку на голову своего учителя, если он у вас есть.

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

Но следующее, что я хочу, это чтобы Артур надел шляпу на Артур голову, Бетти надел шляпу на Бетти голову и Зигги надел шляпуна голове Зигги .Мы не можем ссылаться на этого человека по имени (например, Зигги), потому что это зависит от того, кто это делает.Предположим, мы относимся к нему как к «учителю» и устанавливаем переменную «foo», так что Артур Фу - Артур, а Бетти - Бетти ... но должно быть очевидно, что идея, которую мы действительно выражаем, заключается в том, что Зигги - Зигги, а Джек - ерубыл бы Джек, а Скип foo был бы Скипом ... нам действительно нужно установить "foo"?Нет! У каждого есть фу: это ваше я .Итак, давайте определим неявную переменную «self», которая нигде не объявлена, но всегда ссылается на человека, выполняющего действие.

3.Наденьте шапку себе на голову.

Это работает для Артура, Бетти, Зигги и даже Джека.Это работает для всех.

В вашем коде self относится к Космическому кораблю, к topSpeed ​​которого нужно получить доступ.Вы создаете много Космических кораблей, и каждый должен знать topSpeed ​​этого единственного Космического корабля, который существует (мы знаем, что он делает, потому что он вызывает метод), но не имеет имени (как myWingman.topSpeed) - его self.

2 голосов
/ 16 февраля 2012

Кристиан, я предложу тебе другой способ.Вы говорите, что знаете C, давайте начнем там.Если вам нужно было реализовать дроби, вы бы использовали struct, и давайте предположим, что по какой-то причине вы решили динамически распределять свои дроби.У вас есть что-то вроде этого:

typedef struct { int numerator; int denominator; } Fraction;

Fraction *newFraction(int numer, int denom)
{
   Fraction *result = (Fraction *)malloc(sizeof(Fraction)); // allocate
   result->numerator = numer;
   result->denominator = denom;
   return result;
}

Fraction *multiplyFraction(Fraction *left, Fraction *right)
{

   Fraction *result = (Fraction *)malloc(sizeof(Fraction)); // allocate
   result->numerator = left->numerator * right->numerator;  // multiple (ignoring reduction)
   result->denominator = left->denominator * right->denominator;
   return result;
}

И вы будете использовать это как:

Fraction *half = newFraction(1, 2);
Fraction *twothirds = newFraction(2, 3);

Fraction *onethird = multiplyFraction(half, twothirds); // results is 2/6 as we don't reduce in this example

Это ADT - абстрактный тип данных - стиль программирования.Вы объявляете тип данных, содержимое которого является частным («абстрактная» часть) для функций, которые вы предоставляете, и набор функций.

На базовом уровне то, что делает объектно-ориентированное программирование, это просто инвертирование путиты смотришь на это.Вместо «вызова функции multiplyFraction, передающей две дроби», вы говорите «передать сообщение multiplyFraction вместе с дробью в дробь».Используя синтаксис Objective-C, последняя строка выше:

Fraction *onethird = multiplyFraction(half, twothirds);

становится:

Fraction *onethird = [half multiplyFraction:twothirds];

Под капотом этот «метод send» просто становится «вызовом функции» - Objective-C делаетнемного работы, чтобы найти multipleFraction и затем вызвать его, передав его и half и twoThirds.

Почти там!Теперь, чтобы соответствовать измененному синтаксису для вызова, Objective-C также изменяет синтаксис определения multiplyFraction:

- (Fraction *) multiplyFraction:(Fraction *)right
{

   Fraction *result = [Fraction new]; // allocate
   result->numerator = ????->numerator * right->numerator;
   result->denominator = ????->denominator * right->denominator;
   return result;
}

Но что вы пишете для ????.Поскольку вы увидите, что синтаксис называет только второй параметр (right), для первого имени нет (который был left).Objective-C скрывает передачу этого параметра, каждый метод принимает хотя бы один параметр - это «объект» (а не «ADT»), в который отправляется метод.Ему нужно имя, чтобы вы могли ссылаться на него, это имя self:

- (Fraction *) multiplyFraction:(Fraction *)right
{

   Fraction *result = [Fraction new]; // allocate
   result->numerator = self->numerator * right->numerator;
   result->denominator = self->denominator * right->denominator;
   return result;
}

И это по сути это - self - это имя первого аргумента.

На этой основе строятся объектно-ориентированные языки, например:

  • они имели прямой доступ к переменным "instance" - "поля" оригинала struct;
  • они меняютнемного больше синтаксиса - например, @interface... заменяет struct...;и вместо того, чтобы перечислять методы (функции) после типа (struct) в заголовке, они перечислены внутри его (`@interface);
  • они обычно добавляют наследование (хотя некоторые языки ADT также имеют это);
  • и т. д.

Но под капотом класс Objective-C реализован как C struct...

HTH

0 голосов
/ 15 февраля 2012

Цель C подчеркивает использование геттеров и сеттеров. Чтобы упростить задачу, он даже генерирует геттеры и сеттеры, когда вы @synthesize что-то.

Так

self.topSpeed

обращается к получателю для topSpeed. Если вы опустите часть «self», то это эквивалентно прямому доступу к переменной экземпляра (ivars) (плохая практика).

Причиной наличия подчеркивания перед именем переменной также является четкое различие между переменной экземпляра и получателем для переменной экземпляра. Таким образом, мы не можем случайно обратиться к topSpeed ​​без «self».

Вам нужно использовать self для доступа к переменной во всех местах, кроме:

  • INIT
  • dealloc

Надеюсь, это поможет.

0 голосов
/ 15 февраля 2012

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

Когда вы ссылаетесь на self в методе класса (что очень возможно и приемлемо), вы фактически ссылаетесь на единственный экземпляр, представляющий класс. Вы также можете получить этот экземпляр синглтона, вызвав [Spaceship class]. На практике вы должны использовать self в основном в фабричных методах, когда вам нужно выделить новый экземпляр.

Что вас больше смущает, так это синтаксис относительно других классов. Вы спросили:

Относится ли self.topSpeed ​​к Планете *, а self.nearestWormhole относится к Червоточина *?

Wormhole *nearestWormhole представляет экземпляр класса Wormhole с именем nearestWormhole. Итак, когда вы используете self.nearestWormhole, это указатель на экземпляр класса Workhole. Внутри класса Spaceship вы можете использовать _nearestWormhole или self.nearestWormhole для доступа к этому указателю. Другие классы могут вызывать что-то вроде spaceship.nearestWormhole, использующее метод доступа.

0 голосов
/ 15 февраля 2012

'self' относится к экземпляру текущего класса, т. Е. В вашем примере это будет относиться к экземпляру класса Spaceship. Поскольку «я» всегда ссылается на экземпляр класса, невозможно вызвать себя в методах класса.

...