在 Objective-C 中理解自我

发布于 2025-01-05 20:34:16 字数 1147 浏览 0 评论 0原文

下面的代码来自 iTunes U 的 iPhone 开发课程 Objective-C。我读过苹果文档,除了 self 之外,一切都非常清楚。我有点理解 self 是指向我自己的指针,但这到底是什么意思呢?在下面的代码中,self到底是什么意思?实现文件中 self.topSpeed 和 self.nearestWormhole 之间有什么区别,或者 self 在这两种情况下都引用相同的东西? self.topSpeed 是否指 Planet * , self.nearestWormhole 是否指 Wormhole * ?感谢任何回答的人,我已经学习了 C,现在正在尝试学习 OOP,所以任何输入都会受到赞赏。

(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

The code below is from an iTunes U course on iPhone dev in Objective-C. I've read the Apple documentation and it's all very very clear with the exception of self. I sort of understand self to be a pointer to myself, but what exactly does that mean? In the code below what exactly does self mean? What is the difference between self.topSpeed and self.nearestWormhole in the implementation file or does self refer to the same thing on both occasions? Does self.topSpeed refer to Planet * and self.nearestWormhole refer to Wormhole * ? Thanks to anyone who answers, I've learned C and now trying to learn OOP so any input is appreciated.

(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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

呆萌少年 2025-01-12 20:34:16

self(或 C++ 中的 this)指的是正在执行该方法的对象(或“在其上调用该方法”)。

假设我有一个房间,里面住着三个人,亚瑟、贝蒂和齐吉,还有一盒帽子。我们还定义了

亚瑟的老师是贝蒂。

贝蒂的老师是 Ziggy。

Ziggy 没有老师。

我想向所有三个人发出以下一组指示:

1.把帽子戴在 Ziggy 的头上。

这很简单。 “Ziggy”对亚瑟、贝蒂,甚至对齐吉来说都是同一个人。无论谁遵循此指示,同一个人都会收到帽子。

2.如果你有老师的话,请将帽子戴在老师的头上。

这条指令会根据谁遵循它而产生不同的效果,因为老师对三者中的每一个指称不同的人。但每个人都可以问自己“如果我有老师的话,谁是我的老师?”并找到那个人。

但接下来我想要的是 Arthur 将帽子戴在 Arthur 头上,Betty 将帽子戴在 Betty 头上,Ziggy 将帽子戴在 Ziggy 的头。我们不能直接称呼那个人的名字(比如 Ziggy),因为这取决于谁在做这件事。假设我们把它当作“老师”,并建立一个变量“foo”,这样亚瑟的 foo 是亚瑟,贝蒂的 foo 是贝蒂……但很明显,我们真正表达的想法是 Ziggy 的 foo 是 Ziggy,而 Jack 的 foo是 Jack,Skip 的 foo 是 Skip……我们真的需要建立一个“foo”吗?不! 每个人都有一个 foo:这是你的自我。因此,让我们定义一个隐式变量“self”,它没有在任何地方声明,但始终引用执行该操作的人。

3.把帽子戴在自己的头上。

这适用于亚瑟、贝蒂、齐吉,甚至杰克。它对任何人都有效。

在您的代码中,self 指的是需要访问其 topSpeed 的宇宙飞船。您创建了许多太空飞船,并且每艘太空飞船都需要知道存在的那一艘太空飞船的 topSpeed(我们知道它是因为它正在调用该方法)但没有名称(如 myWingman.topSpeed) - 一个人的 self

self (or this in C++) refers to the object which is executing the method (or "on which the method is being invoked").

Suppose I have a room with three people, Arthur, Betty, and Ziggy, and a box of hats. We also define that

Arthur's teacher is Betty.

Betty's teacher is Ziggy.

Ziggy does not have a teacher.

I want to give the following set of instructions to all three people:

1. Put a hat on Ziggy's head.

This is pretty easy. "Ziggy" means the same person to Arthur, Betty, and even Ziggy. No matter who follows this instruction the same person receives the hat.

2. Put a hat on the head of your teacher, if you have one.

This instruction will have a different effect depending on who's following it, because teacher refers to someone different for each of the three. But each can ask him/herself "who is my teacher, if I have one?" and find that person.

But the next thing I want is for Arthur to put a hat on Arthur's head, Betty to put a hat on Betty's head, and Ziggy to put a hat on Ziggy's head. We can't refer to that person by name (like Ziggy) because it depends on who is doing it. Suppose we treat it like "teacher" and establish a variable "foo" such that Arthur's foo is Arthur, and Betty's foo is Betty… but it should be obvious that the idea we are really expressing is that Ziggy's foo is Ziggy, and Jack's foo would be Jack, and Skip's foo would be Skip… do we really need to establish a "foo"? No! Everyone has a foo: it's your self. So let's define an implicit variable "self" that is not declared anywhere but always refers to the person carrying out the action.

3. Put a hat on the head of your self.

This works for Arthur, Betty, Ziggy, and even Jack. It works for anyone.

In your code self refers to the Spaceship whose topSpeed needs to be accessed. You create many Spaceships and each needs to know the topSpeed of that one Spaceship which exists (we know it does because it's calling the method) but has no name (like myWingman.topSpeed) - one's self.

往昔成烟 2025-01-12 20:34:16

克里斯蒂安,我将为你提供一个不同的解决方案。你说你懂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];

在底层,这个“方法发送”只是变成了“函数调用” - Objective-C 做了一些工作来定位 multipleFraction 然后调用它传递两者halftwoThirds

快到了!现在,为了匹配调用的更改语法,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 是第一个参数的名称。

面向对象的语言建立在这个基础上,例如:

  • 它们可以直接访问“实例”变量 - 原始struct的“字段”;
  • 他们更改了更多语法 - 例如 @interface... 替换 struct...;并且不是在标头中的类型 (struct) 之后列出方法(函数),而是在其内部列出(`@interface);
  • 它们通常会添加继承(尽管某些 ADT 语言也具有继承);
  • 等等。

但在底层,Objective-C 类被实现为 C struct...

HTH

Cristian, I'll offer you a different tack on this. You say you know C, let's start there. If you needed to implement fractions you'd use a struct, and let's assume for some reason you decide to dynamically allocate your fractions. You have something like this:

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;
}

And you'd use it like:

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

This is the ADT - abstract data type - style of programming. You declare a data type whose content is private (the "abstract" part) to the functions you will provide, and a bunch of functions.

At the basic level what object-oriented programming does is just invert the way you look at this. Instead of "call function multiplyFraction passing two fractions" you say "pass the message multiplyFraction, along with a fraction, to a fraction". Using Objective-C syntax the last line above:

Fraction *onethird = multiplyFraction(half, twothirds);

becomes:

Fraction *onethird = [half multiplyFraction:twothirds];

Under the hood this "method send" just becomes a "function call" - Objective-C does a bit of work to locate multipleFraction and then calls it passing it both half and twoThirds.

Almost there! Now to match the changed syntax for the call Objective-C also changes the syntax of the definition of multiplyFraction:

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

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

But what do you write for ????. As you'll see the syntax only names the second parameter (right), there is no name given for the first (which was left). Objective-C hides the passing of this parameter, every method takes at least one parameter - it is the "object" (rather than "ADT") that the method is sent to. It needs a name so you can refer to it, that name is self:

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

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

And this is essentially it - self is the name of the first argument.

Object-oriented languages build upon this base, for example:

  • they had direct access to "instance" variables - the "fields" of the original struct;
  • they change some more syntax - e.g. @interface... replaces struct...; and rather than list the methods (functions) after the type (struct) in the header they are listed inside of it (the `@interface);
  • they usually add inheritance (though some ADT languages have that as well);
  • etc.

But under the hood an Objective-C class is implemented as a C struct...

HTH

不念旧人 2025-01-12 20:34:16

Objective C 强调使用 getter 和 setter。为了让事情变得更简单,当你 @synthesize 某些东西时,它甚至会生成 getter 和 setter。

因此

self.topSpeed

访问 topSpeed 的 getter。如果省略“self”部分,则相当于直接访问实例变量(ivars)(不好的做法)。

之所以在变量名前加下划线,也是为了明确区分实例变量和实例变量的 getter。这样,我们就不会意外地在没有“self”的情况下引用topSpeed。

您需要在所有地方使用 self 访问变量,除了:

  • init
  • dealloc

希望有帮助。

Objective C emphasizes using getters and setters. To make things simpler, it even generates getters and setters when you @synthesize something.

So

self.topSpeed

accesses the getter for topSpeed. If you omit the "self" part, then it is equivalent to accessing the instance variable(ivars) directly (bad practice).

The reason for having a underscore before the variable name is also to make a clear differentiation between instance variable and the getter for the instance variable. This way, we cannot accidentally refer to topSpeed without "self".

You need to use self to access variable in all places except:

  • init
  • dealloc

Hope that helps.

小姐丶请自重 2025-01-12 20:34:16

self 确实是对运行代码的类实例的指针引用。在本例中,self 将是对 Spaceship 类实例的引用。

当您在类方法中引用 self(这是非常有可能且可接受的行为)时,您实际上是在引用代表该类的单例实例。您还可以通过调用[Spaceship class]来获取此单例实例。实际上,当您需要分配新实例时,您主要会在工厂方法中使用 self

您似乎更困惑的是有关其他类的语法。你问:

self.topSpeed 是否引用 Planet * 和 self.nearestWormhole 引用
虫洞*?

Wormhole *nearestWormhole 表示 Wormhole 类的实例,名为 nearestWormhole。因此,当您使用 self.nearestWormhole 时,它​​是指向 Workhole 类实例的指针。在 Spaceship 类中,您实际上可以使用 _nearestWormhole 或 self.nearestWormhole 来访问该指针。其他类可能会调用诸如 spaceship.nearestWormhole 之类的东西,它正在使用访问器。

self is indeed a pointer reference to the instance of the class that is running the code. In this case, self would be a reference to an instance of the Spaceship class.

When you reference self in a class method (which is very possible and an acceptable behavior), you are actually referencing a singleton instance representing the class. You can also get this singleton instance by calling [Spaceship class]. In practice, you'd use self like this mostly in factory methods when you need to allocate a new instance.

What you seem more confused about is syntax regarding other classes. You asked:

Does self.topSpeed refer to Planet * and self.nearestWormhole refer to
Wormhole * ?

Wormhole *nearestWormhole represents an instance of the Wormhole class, named nearestWormhole. So, when you use self.nearestWormhole, that is a pointer to a instance of the Workhole class. Inside the Spaceship class you could actually use _nearestWormhole or self.nearestWormhole to access that pointer. Other classes might call something like spaceship.nearestWormhole, which is using the accessor.

万劫不复 2025-01-12 20:34:16

“self”指的是当前类的实例,即在您的示例中它将指的是 Spaceship 类的实例。因为“self”始终引用类的实例,所以不可能在类方法中调用 self。

'self' refers to the instance of the current class, i.e. in your example it would refer to an instance of the Spaceship class. Because 'self' always refers to an instance of the class, it's not possible to call upon self in class methods.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文