Topics

NSAlert boxes ...

Peter Hudson
 

Hi There

I see from the docs that when you create an NSAlert that you should dispose of it yourself.
My code for creating a simple alert and using it looks like this :-


NSAlert *alert = [[[NSAlert alloc] init] autorelease];
alert.messageText = @:Some message or other”;
[alert addButtonWithTitle:@"Create category"];
[alert addButtonWithTitle:@"Cancel"];

       

NSModalResponse choice = [alert runModal];

Etc …..

Is the use of an autorelease here O.K. ?

It certainly seems to work …..



Peter

Sandor Szatmari
 

Peter,

On Feb 16, 2020, at 08:18, Peter Hudson via Groups.Io <Peter.hudson@...> wrote:

Hi There

I see from the docs that when you create an NSAlert that you should dispose of it yourself.

You may have to dispose of it yourself or maybe ARC will take care of it for you.  If you’re writing MRR (not ARC) code you always have to -release/-autorelease objects you own (create).  

You take ownership of an object when you call -retain or -copy on an existing object, or use -new, -alloc/-init when creating an object.  As far as I’m aware there are no exceptions to these ownership rules.  In MRR you are responsible for calling -release/-autorelease on any object you used these methods to create/copy/take ownership.

These rules are essentially true in ARC too, except the compiler figures out when to call -release so you don’t explicitly call it.

Here is some additional reading on the topic.



My code for creating a simple alert and using it looks like this :-


NSAlert *alert = [[[NSAlert alloc] init] autorelease];
alert.messageText = @:Some message or other”;
[alert addButtonWithTitle:@"Create category"];
[alert addButtonWithTitle:@"Cancel"];

       

NSModalResponse choice = [alert runModal];

Etc …..

Is the use of an autorelease here O.K. ?

It appears to be but I cannot tell what else you do with ‘alert’.  -autorelease says that the object should live for the current scoped autorelease-pool.

So, if this alert panel is not supposed to live beyond the current autorelease-pool, then the code is fine.  If it does live beyond it, then you need to transfer ownership to that scope.

Sandor


It certainly seems to work …..



Peter

Peter Hudson
 

Thanks Sandor.

My use of  alert is very tightly scoped - and only used once - and not passed on.
Many thanks for the link.

Peter



On 16 Feb 2020, at 15:28, Sandor Szatmari <admin.szatmari.net@...> wrote:

Peter,

On Feb 16, 2020, at 08:18, Peter Hudson via Groups.Io <Peter.hudson@...> wrote:

Hi There

I see from the docs that when you create an NSAlert that you should dispose of it yourself.

You may have to dispose of it yourself or maybe ARC will take care of it for you.  If you’re writing MRR (not ARC) code you always have to -release/-autorelease objects you own (create).  

You take ownership of an object when you call -retain or -copy on an existing object, or use -new, -alloc/-init when creating an object.  As far as I’m aware there are no exceptions to these ownership rules.  In MRR you are responsible for calling -release/-autorelease on any object you used these methods to create/copy/take ownership.

These rules are essentially true in ARC too, except the compiler figures out when to call -release so you don’t explicitly call it.

Here is some additional reading on the topic.



My code for creating a simple alert and using it looks like this :-


NSAlert *alert = [[[NSAlert alloc] init] autorelease];
alert.messageText = @:Some message or other”;
[alert addButtonWithTitle:@"Create category"];
[alert addButtonWithTitle:@"Cancel"];
       
NSModalResponse choice = [alert runModal];

Etc …..

Is the use of an autorelease here O.K. ?

It appears to be but I cannot tell what else you do with ‘alert’.  -autorelease says that the object should live for the current scoped autorelease-pool.

So, if this alert panel is not supposed to live beyond the current autorelease-pool, then the code is fine.  If it does live beyond it, then you need to transfer ownership to that scope.

Sandor


It certainly seems to work …..



Peter


Alex Zavatone
 

Why are you making it autorelease?

Run your method, display and dispose of the alert and then while your app is still running click on the Memory Graph Debugger icon in the debug bar.  Look at your allocations and leaks to see if any object of an NSAlert class is still there.

This will tell you if it’s needed or not.

On Feb 16, 2020, at 7:17 AM, Peter Hudson via Groups.Io <Peter.hudson@...> wrote:

Hi There

I see from the docs that when you create an NSAlert that you should dispose of it yourself.
My code for creating a simple alert and using it looks like this :-


NSAlert *alert = [[[NSAlert alloc] init] autorelease];
alert.messageText = @:Some message or other”;
[alert addButtonWithTitle:@"Create category"];
[alert addButtonWithTitle:@"Cancel"];
       
NSModalResponse choice = [alert runModal];

Etc …..

Is the use of an autorelease here O.K. ?

It certainly seems to work …..



Peter


Alex Zavatone
 

On Feb 16, 2020, at 9:37 AM, Peter Hudson via Groups.Io <Peter.hudson=me.com@groups.io> wrote:

Thanks Sandor.

My use of alert is very tightly scoped - and only used once - and not passed on.
Many thanks for the link.

Peter
My expectation is that autorelease is not needed. In any case, if you do use the memory graph debugger to see if it stays allocated or not, and if autorelease is needed, please let us know.

Graham Cox
 

Hi Peter,

FWIW, I’ve always done exactly the same as this too, and have never had an issue.

My understanding (which may be wrong) is that the modal loop that runs the alert temporarily retains it, so you are safe no matter whether the alert is released by you afterwards, or autoreleased.

The point is, your code follows the normal rules. There’s no special case for NSAlert or other modal dialogs, because internal code behind the scenes makes sure the normal rules apply.

—Graham



On 17 Feb 2020, at 12:17 am, Peter Hudson via Groups.Io <Peter.hudson@...> wrote:

Hi There

I see from the docs that when you create an NSAlert that you should dispose of it yourself.
My code for creating a simple alert and using it looks like this :-


NSAlert *alert = [[[NSAlert alloc] init] autorelease];
alert.messageText = @:Some message or other”;
[alert addButtonWithTitle:@"Create category"];
[alert addButtonWithTitle:@"Cancel"];
       
NSModalResponse choice = [alert runModal];

Etc …..

Is the use of an autorelease here O.K. ?

It certainly seems to work …..



Peter


Alex Zavatone
 

On Feb 16, 2020, at 5:04 PM, Graham Cox <graham@...> wrote:

Hi Peter,

FWIW, I’ve always done exactly the same as this too, and have never had an issue.

My understanding (which may be wrong) is that the modal loop that runs the alert temporarily retains it, so you are safe no matter whether the alert is released by you afterwards, or autoreleased.

The point is, your code follows the normal rules. There’s no special case for NSAlert or other modal dialogs, because internal code behind the scenes makes sure the normal rules apply.

—Graham
Sorry, A bit groggy here. If my previous assumption that autorelease isn’t needed is incorrect, then it might be that the instance will be retained until the reference to it is cleared or the enclosing object is deallocated.

In any case, it’s an easy check.

Graham Cox
 

Normal rules require that you either autorelease it or release it when you’re done (assuming manual MM of course).

If no autorelease or release was required, then that would be an exception to normal rules, and NSAlert is not documented as an exception to those rules. However, the alert can stay on screen for a long time, and the event loop is running, albeit modally. That means that autorelease pools are getting drained. So *something* must be keeping the alert alive until the user dismisses it.

—Graham



On 17 Feb 2020, at 10:49 am, Alex Zavatone via Groups.Io <zav@...> wrote:

Sorry, A bit groggy here.  If my previous assumption that autorelease isn’t needed is incorrect, then it might be  that the instance will be retained until the reference to it is cleared or the enclosing object is deallocated.


Peter Hudson
 

Thanks Graham and Alex. 
I had reached the same sort of conclusions - but could not find any corroborative info anywhere ....
Peter 

On 16 Feb 2020, at 23:59, Graham Cox <graham@...> wrote:

Normal rules require that you either autorelease it or release it when you’re done (assuming manual MM of course).

If no autorelease or release was required, then that would be an exception to normal rules, and NSAlert is not documented as an exception to those rules. However, the alert can stay on screen for a long time, and the event loop is running, albeit modally. That means that autorelease pools are getting drained. So *something* must be keeping the alert alive until the user dismisses it.

—Graham



On 17 Feb 2020, at 10:49 am, Alex Zavatone via Groups.Io <zav@...> wrote:

Sorry, A bit groggy here.  If my previous assumption that autorelease isn’t needed is incorrect, then it might be  that the instance will be retained until the reference to it is cleared or the enclosing object is deallocated.


Sandor Szatmari
 

The MRR rules are straight forward and deviating from them because the runloop may retain the alert is asking for trouble in my opinion.  There is no harm in taking ownership of the alert and releasing/autoreleasing it.  I would argue that doing so ensures that any changes in SDK/runtime behavior don’t adversely affect your product.  

Sandor

On Feb 16, 2020, at 19:26, Peter Hudson via Groups.Io <Peter.hudson@...> wrote:


Thanks Graham and Alex. 
I had reached the same sort of conclusions - but could not find any corroborative info anywhere ....
Peter 

On 16 Feb 2020, at 23:59, Graham Cox <graham@...> wrote:

Normal rules require that you either autorelease it or release it when you’re done (assuming manual MM of course).

If no autorelease or release was required, then that would be an exception to normal rules, and NSAlert is not documented as an exception to those rules. However, the alert can stay on screen for a long time, and the event loop is running, albeit modally. That means that autorelease pools are getting drained. So *something* must be keeping the alert alive until the user dismisses it.

—Graham



On 17 Feb 2020, at 10:49 am, Alex Zavatone via Groups.Io <zav@...> wrote:

Sorry, A bit groggy here.  If my previous assumption that autorelease isn’t needed is incorrect, then it might be  that the instance will be retained until the reference to it is cleared or the enclosing object is deallocated.


Graham Cox
 



On 17 Feb 2020, at 12:22 pm, Sandor Szatmari <admin.szatmari.net@...> wrote:

The MRR rules are straight forward and deviating from them because the runloop may retain the alert is asking for trouble in my opinion.  There is no harm in taking ownership of the alert and releasing/autoreleasing it.  I would argue that doing so ensures that any changes in SDK/runtime behavior don’t adversely affect your product.  

Sandor


That’s exactly what I’m saying - you are taking ownership of it with alloc+init.

Then you release it or autorelease it within the scope of setting it up. Normal rules.

But the point is the alert is run asynchronously, so it has to survive long after the scope of where you instantiated and set it up. Long after the next autorelease drain in fact. But that is someone else's problem - the run-loop’s, I’m supposing.

The alternative is to try to take ownership of it until it is finally and completely dismissed, which requires an unbalanced release in the completion block. This not only flags a warning when you compile, but it’s simply unnecessary. Trying to overthink the ownership rules in this case leads to code that is not only complicated, but redundant. The designers of NSAlert have already taken its lifetime into consideraton, so YOUR ownership of it only lasts as long as it takes to instantiate it, set it up, and ask it to run modally.

Peter’s original code, where he autoreleases as part of alloc + init, works absolutely fine.

—Graham


Jon Gotow
 

You're making this far more complicated than it is (talking about running the alert asynchronously, etc).

NSModalResponse choice = [alert runModal];

will run the alert synchronously. Note that it returns a result from this method call, meaning that the call blocks by running its own modal event loop in runModal and only returns after the user has clicked on a button. That means that your autorelease is fine - the scope of this alert is just within the current function, so it'll get cleaned up at the end of the current runloop invocation. That said, you can also just release it manually after you call runModal, like this:

NSAlert *alert = [[NSAlert alloc] init];

alert.messageText = @"Some message or other”;
[alert addButtonWithTitle:@"Create category"];
[alert addButtonWithTitle:@"Cancel"];

NSModalResponse choice = [alert runModal];

[alert release];

If you were to use [NSAlert beginSheetModalForWindow:completionHandler:], then you'd have to worry about lifecycle of the alert extending beyond the current function context.

- Jon

On Feb 16, 2020, at 6:17 AM, Peter Hudson via Groups.Io <Peter.hudson=me.com@groups.io> wrote:

Hi There

I see from the docs that when you create an NSAlert that you should dispose of it yourself.
My code for creating a simple alert and using it looks like this :-


NSAlert *alert = [[[NSAlert alloc] init] autorelease];
alert.messageText = @:Some message or other”;
[alert addButtonWithTitle:@"Create category"];
[alert addButtonWithTitle:@"Cancel"];

NSModalResponse choice = [alert runModal];

Etc …..

Is the use of an autorelease here O.K. ?

It certainly seems to work …..



Peter

Graham Cox
 



On 17 Feb 2020, at 4:27 pm, Jon Gotow <gotow@...> wrote:

If you were to use [NSAlert beginSheetModalForWindow:completionHandler:], then you'd have to worry about lifecycle of the alert extending beyond the current function context.


That is the case I’m talking about and no, it works fine even if you don’t “worry” about the lifecycle.

Just alloc + init + autorelease, then beginSheet…. it works, it has always worked since completion blocks were introduced.

It also works for the -runModal case also, you can autorelease prior to that call and that also doesn’t cause a lifecycle issue.

You can certainly “worry” about extending the lifecycle into the completion block if you want, except the unbalanced release will throw a compiler warning. That in itself shows how unnecessary it is to bother with.

TL;DR: Normal rules apply, stop overthinking this! Just because an NSAlert has some unusual asynchronous lifespan doesn’t mean it needs to be treated any differently from any other object you instantiate.

—Graham




Jon Gotow
 

Oh interesting - it looks like when you call [NSAlert beginSheetModalForWindow:completionHandler:], the alert gets retained by the parent window's attachedSheet property. So you're right, no issues there either. Now that you've brought it up, I have to say I've never worried about the alert being released too early in my code when I use beginSheetModalForWindow - I never gave it much thought because it just worked.

- Jon

On Feb 17, 2020, at 12:54 AM, Graham Cox <graham@...> wrote:

That is the case I’m talking about and no, it works fine even if you don’t “worry” about the lifecycle.

Just alloc + init + autorelease, then beginSheet…. it works, it has always worked since completion blocks were introduced.

It also works for the -runModal case also, you can autorelease prior to that call and that also doesn’t cause a lifecycle issue.

You can certainly “worry” about extending the lifecycle into the completion block if you want, except the unbalanced release will throw a compiler warning. That in itself shows how unnecessary it is to bother with.

TL;DR: Normal rules apply, stop overthinking this! Just because an NSAlert has some unusual asynchronous lifespan doesn’t mean it needs to be treated any differently from any other object you instantiate.