Re: string pointer

Quincey Morris

On Mar 1, 2018, at 04:13 , Gerriet M. Denkmann <g@...> wrote:

But my real problem is this:

BOOL needReason = …
NSString *s;
NSString **stringPointer = needReason ? &s : NULL;
NSUInteger c = [ self computeFor: 42 reason: stringPointer ]; // sometimes want to know the reason

I cannot figure out how to declare the stringPointer without getting compiler warnings.

So, here’s what’s going on. It’s a bit messy.

— A pointer has an ownership attribute, which is __strong by default, but can be explicitly specified as __weak or __autoreleasing. In your code, variable “s” is __strong by default.

— A pointer to a pointer needs to know the ownership of the pointee, and there is no default (in most cases), so you get the error "Pointer to non-const type 'NSString *' with no explicit ownership” for your declaration of “stringPointer”.

— If you specify the actual ownership explicitly:

NSString *__strong*stringPointer = needReason ? &s : NULL;

that error goes away, but there is still an error on the call: “Passing address of non-local object to __autoreleasing parameter for write-back”.

That’s because a pointer to a pointer that’s a method parameter *does* have a default pointee ownership, “__autoreleasing”. (When you added the explicit “__autoreleasing”, you didn’t change anything that wasn’t already assumed.)

— So, you can declare your “computeFor:” method like this:

- (NSUInteger)computeFor: (NSUInteger)arg  reason: (NSString *__strong*)reason

and everything would be just great, and this is the safest route. The only irritation is that every "NSString**” you pass into “computeFor:reason:” is going to need to be explicitly annotated, including those that pass through other methods, so the need to annotate is going to ripple outwards across your code.

— This solution:

NSUInteger c = [self computeFor:42 reason:needReason ? &s : nil];

is AFAIK a bit dangerous. The “computeFor:reason:” method stores an *autoreleased* string pointer into its output parameter, so “s” will end up containing this autoreleased pointer when “needReason” is true. That autoreleased pointer is a ticking time bomb that will explode if it’s still being used somewhere when its autorelease pool is drained  (which could be a *lot* later, leading to hard-to-find bug) without being owned somewhere else. Whether this is a problem depends on what you do with “s” next.

FWIW, all APIs that have a "NSError**outError” parameter have this same danger. (Charles Srstka pointed this out a few months ago.) Our apps work because the unowned pointer usually gets thrown away fast, or gets stored somewhere that gives it an extra retain.

Join to automatically receive all group messages.