【问题标题】:What are difference between *a and **a in objective-c object?Objective-c 对象中的 *a 和 **a 有什么区别?
【发布时间】:2015-05-14 03:42:05
【问题描述】:

我写了以下代码:

NSArray *array = @[@1, @2];

*array**array如何输出,它们有什么区别?

【问题讨论】:

  • 显然,“单*”表示“指向...的指针”,而“双*”表示:“指针指向指针到。”在很多情况下,就像这样,“你最好的朋友是一支铅笔和一张纸。” 把它画出来……在纸上做一个图表。
  • NSArray* array 是指向 NSArray 实例的指针。 NSArray** array 是一个指向指向 NSArray 实例的指针。
  • 我鼓励您从字面上取消引用 array。你会学到一些有趣的语言

标签: objective-c pointers


【解决方案1】:

有一些答案,但我认为它们都对您没有真正的帮助,因为它们描述了技术含义。 让我们看一下第一个声明:

NSArray *array …;

当有人谈论这段代码时,你会发现arrayNSArray 的实例对象。甚至每个有经验的 Objective-C 开发人员都知道这句话的真正含义,这是完全错误的。正确的说法是什么?

A. NSArray的实例对象

一个实例对象有一个状态,基本上是一组存储的数据。为此,它需要在创建对象时保留的内存。您不直接处理它,但它在+alloc 内部完成。这是在程序运行时在运行时显式完成的(“在堆上”、“堆分配”)。

… [NSArray alloc] … // Get memory for an instance object of type NSArray

您仅通过其地址(占用的内存区域的第一个内存单元的编号)来寻址此类对象。 (每个存储单元都有一个编号,称为地址。是的,这类似于通过街道上的房屋编号来寻址房屋中的居民。因此,您可以将记忆想象成一条非常非常长的街道。)

但是像array 这样的标识符只在编译时存在,并在程序编译时被删除。因此很明显,像array 这样的标识符永远不会表示实例对象。

简短版:实例对象是内存区域,仅通过第一个内存单元(位置)的编号进行寻址。

B.指向实例对象的指针(引用)

但是如果一个实例对象在运行时通过它的编号来寻址,我的代码如何处理它?诀窍是数字存储在一个变量中。 (看不完全正确的C标准。他们说它存储在一个对象中。但是这些对象与Objective-C对象无关,我将重点关注变量,对象的子类型。)

所以你可以有一个变量来存储实例对象的内存位置。这样的变量称为指针变量。它是用额外的* 声明的。所以

NSArray * array;

表示:标识符为array的指针变量,用于存储NSArray类型的实例对象的位置。

(地址是数字。它们是整数。因此指针变量和整数之间存在联系。您可以对这些数字应用计算,称为“指针算术”。在某些情况下,这对 C 开发人员很重要,但是不适合作为 Objective-C 开发人员的你。)

这个变量的内存不是用+alloc显式保留的,而是在你进入你的代码区域时隐式保留的,在该区域声明了变量。 (再次不完全正确,但足以解释这个解释。)所以让我们再看一下对象创建的一个非常简单的版本:

- (void)method
{
  NSArray *array = [NSArray alloc];
}

该语句右边为对象实例保留内存并返回一个数字,即内存区域的地址。此编号分配给名为array 的引用。该引用的内存(它存储一些东西,因此需要内存)通过其定义隐式保留。

指向对象的指针通常称为引用。

短版:所以array是一个实例对象的引用,存储了实例对象的地址。

C.指向实例对象指针的指针

好的,我们有一个实例对象,它占用内存来存储对象状态,通过它的内存位置(地址)来寻址。然后我们有一个存储该地址的变量,引用array。您可以通过其标识符来解决这个问题。

但有时它也很有用——我将在下面有一个例子——通过它的地址来寻址引用变量。您可以使用地址运算符& 获取变量的地址。

&array

你得到的是:一个变量的地址存储了一个实例对象的地址。这种双重间接的类型(“地址……地址……”)是

NSArray ** array;

这是因为在变量定义中* 表示“地址”。

短版:指向引用的指针是一个变量,它存储了一个变量的地址,该变量存储了一个实例对象的地址。它用** 声明。 (是的,你可以有更多级别的间接......不,这不太容易理解。)

D.引用指针的用例

在 Objective-C 中通常不需要这种双重间接。但是有一个重要的用例,一个错误输出参数。为了理解这一点,我们将研究一个具有单个间接参数的方法,如您所知:

- (BOOL)methodThatCanProduceAnError:(NSError*)error
{
   …
   error = [NSError alloc] … // Create an error object and store its address to the reference variable error.
   return NO;
   …
}

此方法应通过其error 参数发出错误。

你用这样的代码“调用”这样一个方法:

…
NSError *error; // A reference variable pointing to an instance object of type NSError
error = nil; // I do not have an error, so it points to "nothing".
[anInstance methodThatCanProduceAnError:error];

会发生什么?您将实例对象的地址作为参数传递给该方法。你通过nil 说“我没有错误”。这很清楚,因为该方法应该传递对实例对象的引用。

所以有趣的部分在方法内部,当它创建错误对象并尝试将其传递出去时。 这不起作用!

在 Objective-C 中,参数总是按值传递。这意味着,“调用代码”中的参数值被获取并分配给“被调用代码”内的 new 变量。这个new 变量称为参数变量。当方法试图改变变量的值时

error = [NSError alloc] … // Create an error object and store its address to the reference variable error.

它仅更改方法内的变量副本的值。新的引用永远不会找到方法的出路,调用代码中的变量error 保持不变。在“调用代码”中,error 仍然具有旧的nil 值。

所以我们需要一种方法来改变“调用代码”中引用的内容。我们通过将引用的地址传递给方法来做到这一点:

- (BOOL)methodThatCanProduceAnError:(NSError**)error // double indirection
{
   …
   *error = [NSError alloc] … // *error is a reference to an object
   return NO;
   …
}

首先在方法头中,我们声明一个双重间接的参数变量:引用的地址。其次我们将实例对象的地址分配给错误指向的位置。 (这是由* 完成的。)赋值不是对参数变量进行,而是对位置进行,参数变量指向。

因此我们可以传递引用的地址:

NSError *error; // A reference variable pointing to an instance object of type NSError
error = nil; // I do not have an error, so it points to "nothing".
NSError ** pointerToError = &error; // The address of the reference.
[anInstance methodThatCanProduceAnError:pointerToError];

现在该方法更改了引用变量的内容。错误传递出去。

【讨论】:

  • array 是 NSArray 的实例有什么问题?在这里说您需要知道的所有内容,
  • 不,并非总是如此。只需有一个可变数组和两个引用array1array2。 “array1 是 NSArray 的一个实例”和“array2 是 NSArray 的一个实例”涵盖了这样一个事实:通过一个 reference 更改 instance 也会更改“other”实例.通常你没有这样的问题,所以说“数组是一个实例”就足够了。但这就是我在 A 中所说的。
  • 我不确定您是否已经明确了实例的定义。实例不是不可变的引用,它是类对象的特定实例化。它存在于堆中是无关紧要的。它是指向某个内存块的指针是一个实现细节。指针别名除了为某些消息提供相同的接收者外,什么也不做。你在那个被删除的答案中是对的,Objective-C 不是 C。你应该在比单独使用 C 更高的层次上思考。
  • 不,因为两个不同的引用引用一个单一的实例对象的行为仍然在 Objective-C 中并且是一个重要的事实,因为使用一个引用来改变一个对象会在你使用第二个时反映出来读取对象的引用。这表明引用和对象之间仍然存在对开发人员很重要的区别。如果不知道参考是什么,您就无法解释这种影响。 (对于不可变的实例,您将永远找不到效果。)
  • 绝对正确。但是,“对象”的定义比您想象的要宽松。在使用 Objective-C 时要小心。这就是为什么我不希望你被锁定在引用、实例和指针星的这个角落;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-03
  • 2010-11-27
  • 1970-01-01
  • 1970-01-01
  • 2018-06-27
  • 2013-12-24
  • 1970-01-01
相关资源
最近更新 更多