Re: string pointer


Quincey Morris
 

On Mar 1, 2018, at 11:18 , Steve Mills <sjmills@...> wrote:

I disagree that it's dangerous. It's the standard way code is written when a parameter is passed back by reference. […]

(I tried to find Charles’s post about this, but didn’t succeed.)

Everything you said after the first sentence is absolutely true. That is the standard way, and your suggestion helped rewrite the code according to  the standard way without a lot of fuss.

But it’s still dangerous. You can Try It at Home™. Put this code in a new app’s delegate:

- (void) computeReason: (NSObject **) reason
{
*reason = [[NSObject alloc] init];
}

- (void) applicationDidFinishLaunching: (NSNotification*) notification
{
NSObject *s;
// @autoreleasepool {
[self computeReason: &s];
// }
NSLog (@"%@", s);
}

Run it and it works. Now comment out the 2 commented lines. Run it and it crashes.

That’s because “s” contains an autoreleased pointer. It’s fine so long as nothing drains the autorelease pool, but if something does, it fails. The fact that “s” is supposed to be __strong is (unfortunately) not respected by the called method, which thinks it’s __autoreleasing.

Normally, it’s not a problem, because the drain of the autorelease pool is up at the main event loop, long after these methods have returned. However, if the pointer ends up in a different pool (as in this contrived example) with a shorter lifetime, that’s bad.

This example is not so very contrived, though. It isn’t uncommon to use a local autorelease pool for compute-bound loops, and if any errors happen during the loop the app can crash. Also, if you send pointers off to GCD queues, the autorelease pool lifetimes are hard to predict.

BTW, the original code would *never* fail, for an unrelated reason. The pointer in that case was to a literal NSString, which isn’t reference counted, and is alive forever.


Join cocoa@apple-dev.groups.io to automatically receive all group messages.