跳过在您的特定情况下您尝试做的设计是否是好的设计的问题,我将尝试解释您为什么会出错。如果这太简单了,请道歉,但希望它对某人有所帮助。
这个问题与属性本身无关,而与变量有关,要理解这个问题,我们需要了解变量是什么以及它们在 ARC 下的行为方式.完成此操作后,您在使用 NSOutputSteam ** 时会出现错误的原因如下。
我们经常提到“强引用”或“弱引用”,但这些术语确实有点用词不当。引用既不强也不弱,它们只是引用; strong 和 weak 指的是引用是如何被变量处理和存储在变量中的。
变量只是一个命名的盒子,它能够保存某种类型的值。例如声明:
int x;
正在请求获取一个能够存储int 值的盒子,并将其命名为x。作业:
x = 42;
请求将整数 42 的(计算机表示)存储在名为 x 的框中。在处理值类型时,即计算机表示被传递并直接存储在变量中的类型,然后就是故事的结尾,我们几乎知道所有关于变量的知识。
但是,当存储在变量中的值是引用时,情况会有所不同。例如声明:
NSOutputStream *outStream;
是一个能够存储对NSOutputSteam 的引用的盒子,而不是能够存储实际NSOutputStream 的盒子。
一个粗略的类比是将房子的地址存储在一个盒子里,而不是把一个实际的房子存储在一个盒子里(这需要一个更大的盒子!)。
进一步类比,您可以有许多不同的盒子,它们都存储同一个房子的地址,但您不能在多个盒子(位置)中拥有同一个房子 - 房子是独一无二的。在编程中,对象是独一无二的,但您可以将同一个对象的引用存储在多个不同的框中。
当房屋不再需要时,它可能会被拆除,清理地面,并在同一块土地上建造新的不同房屋。 (是的,我们现在稍微扩展了类比;-)) 编程中的对象也是如此;它们被创建,有生命周期,并且在不再需要时被拆除 - 也就是 垃圾收集、deallocated 等。
系统如何知道某个对象何时不再需要?不同的编程语言做到这一点的方式不同。在 Objective-C ARC 中,它由 变量 上的强属性和弱属性处理(而不是引用上的)。这些属性本质上是关于当新值存储在变量中或变量本身被破坏时要做什么的协议 - 例如当其拥有的方法方法返回(对于局部变量)或其拥有的对象被销毁时(对于实例变量)。
如果声明变量时没有指定属性,则假定为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 主要将此协议用于通过将值存储到传递框中来返回值的方法,最常见的情况是用于返回错误对象的NSError ** 参数。 为什么这个协议存在是另一回事(这个答案已经很长了!)如果你愿意,可以阅读NSError and __autoreleasing以获取更多详细信息。
现在发表你的声明:
return &_outputStream;
您正在尝试返回类型为(默认)__strong NSOutputStream * 的框 _outputStream,即该框使用强协议。但是,您已声明您的函数(默认情况下)返回一个 __autoreleasing NSOutputStream * 类型的框。由于值如何存储在盒子中取决于协议,并且两种协议不同,因此会出现错误。
修复
因此,如果您将函数定义为:
- (NSOutputStream * __strong *)outputStreamPtr
{
return &_outputStream;
}
那么编译错误就会消失。 ARC 也会知道如何在返回的框中存储一个值,一切都应该很好......
希望在这个相当长的回答之后,您知道原因并且不会比以前更加困惑!
当然,如开头所述,您是否应该首先退回一个盒子是一个不同的问题,我将在此处跳过。