0

A function call in class A requires and argument as (NSOutputStream **)

the stream i want to pass is hold in a property of class B

@property (nonatomic, strong) NSOutputStream * outputStream;

i wanted to make this property of class B accessible like this to the outer world (for class A)

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

but i'am getting

Returning 'NSOutputStream *__strong *' from a function with result type 'NSOutputStream *__autoreleasing *' changes retain/release properties of pointer

How to pass a property linked ivar memory address outside of the class?

Peter Lapisu
  • 19,915
  • 16
  • 123
  • 179

2 Answers2

1

Skipping the issue of whether what you are trying to do is good design in your particular case I’ll try to explain why you get an error. If this turns out to be too simplistic, apologies, but hopefully it helps someone.

The issue has nothing to do with properties per se but with variables, and to understand the issue we need to look at what variables are and how they behave under ARC. Once we've done that why you get an error with your use of NSOutputSteam ** follows.

We often refer to a “strong reference” or a “weak reference” however the terms are really somewhat of a misnomer. References are neither strong or weak, they are just references; strong and weak refer to how references are handled by, and stored in, variables.

A variable is just a named box which is capable of holding a value of some type. For example the declaration:

int x;

is requesting a box capable of storing an int value be obtained and given the name x. The assignment:

x = 42;

is requesting that the (computer representation of the) integer 42 be stored in the box named x. When dealing with value types, that is types where the computer representation is passed around and stored directly in variables then that is the end of the story, we know just about all we need to know about variables.

However things become a bit different when the value stored in a variable is a reference. For example the declaration:

NSOutputStream *outStream;

is a box capable of storing a reference to an NSOutputSteam, not one of capable of storing an actual NSOutputStream.

A rough analogy is storing the address of a house in a box compared to storing an actual house in a box (which would require a much bigger box!).

Taking the analogy further you could have lots of different boxes all storing the address of the same house, but you cannot have the same house in multiple boxes (locations) - houses are unique. And back in programming objects are unique, but you can store references to the same object in multiple different boxes.

When a house is no longer required it may be demolished, the ground cleared, and a new different house built on the same land. (Yes we're stretching the analogy a bit by now ;-)) The same is true of objects in programming; they are created, have a lifetime, and when no longer needed are demolished - aka garbage collection, deallocated, etc.

How does the system know when an object is no longer required? Different programming languages do this is different ways. In Objective-C ARC it is handled by the strong and weak attributes on the variables (and not on the references). These attributes are essentially protocols on what to do when a new value is stored in a variable, or a variable is itself destroyed - e.g. when its owning method method returns (for local variables) or its owning object is destroyed (for instance variables).

If no attribute is specified when a variable is declared then strong is assumed, so the above example is actually:

__strong NSOutputStream *outStream;

The strong protocol for storing a reference in a variable is to register an ownership interest in the object being reference, and as long as there is an ownership interest in an object ARC will not destroy that object. So when the assignment:

outStream = w;

is processed the compiler, seeing that outStream has the strong attribute:

  • registers an ownership interest in the object referenced by w;
  • if there is already an existing reference stored in outStream then it de-registers an ownership interest in the object referenced; and
  • stores the reference into outStream.

This is clearly more involved than the int case above, which just stores the bits into the box. And the protocol is different depending on the attribute. For example, if the attribute is weak (and being liberal with our analogous terms) the protocol is:

  • register to receive notification of the destruction of the object referenced by the incoming reference;
  • deregister to receive notification of the destruction of the object referenced by the existing reference; and
  • store the reference into the variable.

When an object destruction occurs any variable which has registered to receive notification of that object's destruction has the value nil stored in it.

These two protocols are quite different, and so the simple assignment:

a = b;

where a is a reference-valued variable is handled quite differently depending on the attribute attached to a (and not to the reference stored in a).

Your Case

When you declare a parameter of type NSObjectStream **, or generally <any object> **, you are not passing a reference to an NSObjectStream but a reference to a variable which in turn holds a reference to an NSObjectStream (or generally <any object>) - think of it as passing the box rather than the value in the box.

The reason for doing this is of course so that the called method can store a value into the box. However from the above we now that how a value is stored into a box under ARC depends on the attribute; strong, weak, etc.; attached to the box. So the called method needs to know not just the type of value that goes into the box, but also the protocol to use for storing it.

As with the variable declaration if no protocol is given then there is a default, the type of the parameter - as shown in your error message - is in fact:

NSObjectStream * __autoreleasing *

which means “a box which can hold a reference to an NSObjectStream and uses the protocol autoreleasing”.

The autoreleasing protocol is different from both the strong and weak ones, in brief it stores the new reference without declaring any ownership interest and it does nothing with the old reference – e.g. its a simple assignment just like for value types. Objective-C uses this protocol primarily for methods which return a value by storing it into a passed box, the most common case being NSError ** parameters used to return error objects. Why this protocol exists is another story (this answer is already rather long!) you can read NSError and __autoreleasing for further details if you wish.

Now with your statement:

return &_outputStream;

you are attempting to return the box _outputStream whose type is (by default) __strong NSOutputStream *, that is the box uses the strong protocol. However you have declared your function (by default) to return a box of type __autoreleasing NSOutputStream *. As how a value is stored in a box depends on the protocol, and the two protocols are different, you get an error.

The Fix

So this long answer get us to a fix, if you define your function as:

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

then the compile error will go away. Also ARC will know how to store a value in the returned box, and all should be well...

Hopefully after this rather long answer you know why and are not just more confused than before!

Of course, as stated at the start, whether you should be returning a box in the first place is a different issue, and one I’ll skip here.

Community
  • 1
  • 1
CRD
  • 52,522
  • 5
  • 70
  • 86
0

What you are trying to do is bad in the first place (breaking encapsulation etc.). Together with automatic reference counting it's a nightmare.

You can always return a selector for a setter.

gnasher729
  • 51,477
  • 5
  • 75
  • 98