属性 的返回地址

Returning address of property

class 中的函数调用要求和参数为 (NSOutputStream **)

我要传递的流保存在 属性 的 class B

@property (nonatomic, strong) NSOutputStream * outputStream;

我想让 class B 的 属性 像这样被外界访问(对于 class A)

- (NSOutputStream **)outputStreamPtr {
    return &_outputStream;
}

但我得到了

从结果类型为 'NSOutputStream *__autoreleasing *' 的函数返回 'NSOutputStream *__strong *' 会更改指针的 retain/release 属性

如何在class之外传递一个属性链接的ivar内存地址?

您首先尝试做的事情很糟糕(破坏封装等)。与自动引用计数一起,这是一场噩梦。

您始终可以 return setter 选择器。

跳过你尝试做的事情在你的特定情况下是否是好的设计的问题,我将尝试解释你为什么会出错。如果结果证明这太简单了,我们深表歉意,但希望它能对某人有所帮助。

这个问题与属性本身无关,但与变量有关,要理解这个问题,我们需要看看什么变量是什么以及它们在 ARC 下的行为。一旦我们这样做了,为什么您在使用 NSOutputSteam ** 时会出现错误。

我们经常提到“强引用”或“弱引用”,但这些术语确实有点用词不当。引用既不强也不弱,它们只是引用; strong 和 weak 指的是引用如何被变量处理和存储在变量中。

变量只是一个命名的盒子,它能够保存某种类型的值。例如声明:

int x;

正在请求获得一个能够存储 int 值的盒子并命名为 x。作业:

x = 42;

请求将(计算机表示的)整数 42 存储在名为 x 的框中。在处理值类型时,即计算机表示形式被传递并直接存储在变量中的类型,然后故事就结束了,我们知道了关于变量的所有我们需要知道的。

然而,当存储在变量中的值是 引用 时,情况会有所不同。例如声明:

NSOutputStream *outStream;

是一个能够存储 NSOutputSteam 的引用的盒子,而不是能够存储实际 NSOutputStream.

的盒子

一个粗略的类比是将房子的地址存储在一个盒子中,而不是将实际的房子存储在一个盒子中(这需要一个更大的盒子!)。

进一步打个比方,你可以有很多不同的盒子都存储同一所房子的地址,但你不能在多个盒子(位置)中拥有同一所房子 - 房子是独一无二的。回到编程中 对象 是唯一的,但您可以将对同一对象的引用存储在多个不同的框中。

当房子不再需要时,它可能会被拆除,清理地面,并在同一块土地上建造新的不同 房子。 (是的,我们现在稍微扩展了类比;-))编程中的对象也是如此;它们被创建,有生命周期,当不再需要时被拆除——又名垃圾收集释放

系统如何知道何时不再需要某个对象?不同的编程语言以不同的方式做到这一点。在 Objective-C ARC 中,它由变量 上的强属性和弱属性 (而不是引用)处理。这些属性本质上是关于当新值存储在变量中或变量本身被销毁时要做什么的协议 - 例如当其拥有的方法 method returns(对于局部变量)或其拥有的对象被销毁(对于实例变量)时。

如果声明变量时没有指定属性,则假定为strong,所以上面的例子实际上是:

__strong NSOutputStream *outStream;

用于在变量中存储引用的强协议是在被引用的对象中注册一个所有权权益,并且只要对象 ARC 中存在所有权权益不会破坏那个对象。所以赋值时:

outStream = w;

被编译器处理,看到outStream有strong属性:

  • w 引用的对象中注册所有权权益;
  • 如果 outStream 中已经存储了一个现有引用,那么它会注销所引用对象的所有权权益;和
  • 将引用存储到 outStream

这显然比上面 int 的情况更复杂,后者只是将位存储到框中。协议因属性而异。例如,如果属性很弱(并且对我们的类似术语很自由),则协议是:

  • 注册以接收销毁传入引用所引用对象的通知;
  • 注销以接收现有引用所引用对象的销毁通知;和
  • 将引用存储到变量中。

当对象销毁发生时,任何已注册接收该对象销毁通知的变量都会将值 nil 存储在其中。

这两个协议有很大的不同,所以简单赋值:

a = b;

其中 a 是一个引用值变量,根据附加到 a 的属性(而不是存储在 a 中的引用),处理方式完全不同。

您的案例

当您声明类型为 NSObjectStream ** 或通常为 <any object> ** 的参数时,您传递的不是对 NSObjectStream 的引用,而是对变量的引用,该变量又包含一个引用 NSObjectStream(或通常 <any object>)- 将其视为传递框而不是框中的值。

这样做的原因当然是为了让调用的方法可以将值存储到框中。然而,从上面我们现在可以看出,一个值如何存储到 ARC 下的盒子中取决于属性;强、弱等;附在盒子上。所以被调用的方法不仅需要知道盒子里的值的类型,还要用于存储它的协议。

与变量声明一样,如果没有给出协议,则有一个默认值,参数的类型——如您的错误消息所示——实际上是:

NSObjectStream * __autoreleasing *

意思是“一个可以保存对 NSObjectStream 的引用并使用协议 autoreleasing 的盒子”。

自动释放协议不同于强协议和弱协议,简而言之,它存储新引用而不声明任何所有权权益,并且它不对旧引用执行任何操作——例如它是一个简单的赋值,就像值类型一样。 Objective-C 主要将此协议用于通过将值存储到传递框中来 [=​​160=] 值的方法,最常见的情况是 NSError ** 用于 return 错误对象的参数。 为什么这个协议存在是另一个故事(这个答案已经很长了!)如果你愿意,你可以阅读NSError and __autoreleasing了解更多细节。

现在发表您的声明:

return &_outputStream;

您正在尝试 return 类型为(默认)__strong NSOutputStream * 的框 _outputStream,即框使用强协议。但是,您已将函数(默认情况下)声明为 return 类型为 __autoreleasing NSOutputStream * 的框。由于值在框中的存储方式取决于协议,而这两个协议不同,因此会出现错误。

修复

如果您将函数定义为:

- (NSOutputStream * __strong *)outputStreamPtr
{
   return &_outputStream;
}

然后编译错误就会消失。此外,ARC 将知道如何在 returned 框中存储一个值,并且一切都应该很好...

希望在这个相当长的回答之后你知道为什么并且不只是比以前更困惑!

当然,正如开头所述,是否应该首先return打盒子是另一回事,我将在此跳过。