1

I have several places where I need to display an alert and handle the dismiss operation in the same way, namely take them to the same view controller. Instead of duplicating the alert specific code in all those places, I created a separate class like below:


AlertUtility.h:
@interface AlertUtility : NSObject {
}
- (void) displayAlert;
@end

AlertUtility.m:
@implementation AlertUtility {
- (void) displayAlert {
   UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:... message:... 
      delegate:self cancelButtonTitle:... otherButtonTitles:nil] autorelease];
   [alert show];
}

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
   // Create another view controller and display its views
   [self release] // Release current object because I'm not releasing it where I create it
}

}

Where I need to use this alert (i.e. in MyViewController), I have the following:


MyViewController.m:
AlertUtility *utility = [[AlertUtility alloc] init];
[utility displayAlert];

As you can see, I am not releasing the utility object here (which I should since I own it), but rather in the didDismissWithButtonIndex method of the AlertUtility class. I am clearly missing something here.

I've tried autoreleasing the utility object but then by the time the didDismissWithButtonIndex method is 'called' on the utility object, that object was already released (due to the autorelease).

I've tried making the displayAlert method static (and calling it with [AlertUtility displayAlert];), but then the didDismissWithButtonIndex is never called.

Should I just release the current object from within the didDismissWithButtonIndex method, like I do now, or is there a way to release it in MyViewController instead (without creating a AlertUtility property for the current class)?

Thanks!

EDIT Should I use the Singleton pattern instead maybe?

Mihai Fonoage
  • 478
  • 2
  • 8
  • 23
  • I don't think Singleton is a good pattern for this (just a feeling - but then it is very rarely a good pattern at all). You could always build a subclass of UIAlertView for your purpose. – Eiko Jul 07 '10 at 13:15
  • I'm not doing anything custom or specific with the alert. I don't see how subclassing it would help, but maybe I'm missing something. – Mihai Fonoage Jul 07 '10 at 13:18
  • Don't use a singleton. It won't solve your retention problem (singletons still have to be retained) and nothing in the alert management requires a singleton. Singletons should only be used when it is logically critical that one and only one instance of a class exist at one time. Any other use is singleton abuse and will lead to complexity and grief. – TechZen Jul 07 '10 at 14:01

3 Answers3

1

Calling [self release] is a big no-no because no object has information about what other object refer to it. The retention system is intended to make sure that if ObjA needs ObjB, that ObjB will remain in memory until ObjA no longer needs it. If ObjB releases itself, the entire system breaks down.

In this case, the UIAlert itself retains the AlertUtility object as it's delegate so you can release the AlertUtility as soon as your done with it just as you would a UIAlert.

So,this is fine:

AlertUtility *utility = [[AlertUtility alloc] init];
[utility displayAlert];
[utility release];   // released here, but still alive

It's not a problem if the AlertUtility object is still alive past the release. A release doesn't necessarily kill an object. It merely says that the object sending the release message has no further need of the released object. Each retaining object just has the responsibility to balance all retains (including implicit ones like init) with a release. Past that the retaining object has no interest and no responsibility for whether the system keeps released object alive or not.

After all, any single object may be retained by multiple retaining objects each with no knowledge of the other. In this case the AlertUtility is retained once by the ViewController with init. Then it is retained again by the UIAlert as a delegate. (Edit: UIAlert assigns it delegate)When ViewController releases the AlertUtility object, it will remain alive as long as the UIAlert needs it and vice versa.

Since the UIAlert object is retained by the UIWindow when it is displayed this means that the AlertUtility object will remain alive until after the UIAlert is dismissed. (Edit: this is valid only for the view, not the AlertUtility delegate)

You would not normally have a UIAlert delegate whose existence is solely dependent on that of the UIAlertitself. Usually, the UIAlert delegate is the view controller that evokes the alert. That way, you don't have to worry about the delegate dying before it completes the task associated with the alert.

Eiko
  • 25,601
  • 15
  • 56
  • 71
TechZen
  • 64,370
  • 15
  • 118
  • 145
  • Thanks for the reply. Are you just suggesting to release the AlertUtility object (which I tried by either explicitly releasing it OR by using autorelease, but in both cases, by the time the didDismissWithButtonIndex method is sent to the utility object, that object was already released)? In my particular case, the AlertUtility object can't be alive after releasing it since the retain count would be 0 (I'm not sending/returning that object anywhere). – Mihai Fonoage Jul 07 '10 at 13:35
  • ObjA should send a release to ObjB as soon as it has no more need for ObjB. If you find yourself in a situation in which you still need ObjB alive even though ObjA no longer needs it, then you have a design problem. Your design problem here is that your ViewController(VC) creates the AlertUtility(AU) object but the VC has no means of knowing when the AU object has completed its task so the VC has no means of knowing when it is done with the AU object so that it can send release. – TechZen Jul 07 '10 at 13:55
  • Basically, you have a circular retain. AU creates and retains the UIAlert(UA) which retains the AU has a delegate. The VC releases the AU which is then retained only by the UA. Shifting the UA release to the end AU `didDismissWithButtonIndex` will prevent the AU from dying until it is done. However, you really just shouldn't have an AU object in the first place. It really doesn't serve a purpose and just adds complexity. – TechZen Jul 07 '10 at 13:59
  • It is a design problem, you are right, but the only solution is see is having duplicate code in every VC where I need that alert (and just use a UIAlertView directly). That is the way I started all this, but I thought I could refactor that part of the code. – Mihai Fonoage Jul 07 '10 at 14:04
  • @TechZen: Can you please clarify your downvote on my answer? Maybe I'm missing something, but you basically suggest the same. Maybe you missed that I did [self release] to balance the [self retain] in -displayAlert:? It seems perfectly fine to me. His view controller is not longer interested, so after alloc/init it can release it. But the AlertUtility will get the call back, and thus needs to retain itself, because.... You say here that delegates are retained. This is *not* a good pattern and rarely holds. They are assigned, and not retained (Check out WWDC Session 116 Model-View-Controller). – Eiko Jul 07 '10 at 14:10
  • For whoever is interested why delegates are not retained, please follow this discussion: http://stackoverflow.com/questions/918698/why-are-objective-c-delegates-usually-given-the-property-assign-instead-of-retain – Eiko Jul 07 '10 at 14:26
  • @Eiko -- see comments on your answer. – TechZen Jul 07 '10 at 14:42
  • @Mihai Fonoage -- Since alerts should be customized and they need to be tied specific code, it doesn't make much since to create an additional class wrapper for them. They really need to communicate directly back to the object whose logic triggers the alert. The class is really as convient and encapsulated as functionally possible already. – TechZen Jul 07 '10 at 14:45
  • @Eiko -- Just because it might be ideal that delegates are not retained does not mean they won't be even in Apple code. Nothing in the API enforces this and you shouldn't rely on it being so. In this case, your right: UIAlert does assign rather than retain. However, that doesn't alter the basic problem here of releasing self. – TechZen Jul 07 '10 at 14:48
  • @TechZen: Generally you are right regarding alerts, but for my specific case, they are not tied to the code/controller where they are created because the action on them takes you to a the same view, no matter which VC created the alert. Nevertheless, I believe when you say that calling retain/release on self is a bad practice, hence I will change the code to its initial form and learn to live with a little duplicate code. – Mihai Fonoage Jul 07 '10 at 14:58
  • Your code will just break. Period. If you release AlertUtility, that instance is away when the user clicked the button. The only way to get this clean is to retain it somehwere - and this should probably be in the same class that will release it. And I cannot see another place than AlertUtility. I wonder why you defend a code that obviously crashes? – Eiko Jul 07 '10 at 15:03
  • I'm not sure if your comment is addressed to me. If it is, what I will actually do is not implement a wrapper around the alert (get rid of AlertUtility), but actually use it directly in each VC, and have each VC implement the UIAlertViewDelegate protocol. The decision comes because of the comment that one should not call retain/release on self. – Mihai Fonoage Jul 07 '10 at 15:36
0

Your pattern works, although I agree releasing it "elsewhere" is ugly and I try to avoid this. You could make the following approach:

// AlertUtility.h:
@interface AlertUtility : NSObject {
}
- (void) displayAlert;
@end

// AlertUtility.m:
@implementation AlertUtility {
- (void) displayAlert {
   UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:... message:... 
      delegate:self cancelButtonTitle:... otherButtonTitles:nil] autorelease];
   [alert show];
   [self retain];   // explicitly retain this (self) object
}

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
   // Create another view controller and display its views
   [self release] // Release current object because you retained it earlier here
}

}

And later:

 // MyViewController.m:
    AlertUtility *utility = [[AlertUtility alloc] init];
    [utility displayAlert];
    [utility release];   // released here, but still alive

Edit: removed the release on the alert, as it was autoreleased.

Eiko
  • 25,601
  • 15
  • 56
  • 71
  • -1 You never send release or retain to self. Both are used by external objects to tell the system whether they need the object or not. No object needs to retain itself and therefore has no need to release itself. – TechZen Jul 07 '10 at 12:53
  • Then you can maybe give us a better pattern? I am not fond of this workaround either... – Eiko Jul 07 '10 at 12:56
  • releasing it does not work (same problem as if I would call autorelease manually - by the time the didDismissWithButtonIndex is sent, the object has already been released). – Mihai Fonoage Jul 07 '10 at 13:00
  • Then your problem must be hidden somewhere else... this code is *not* over-releasing. – Eiko Jul 07 '10 at 13:05
  • I didn't say it was over-releasing. It's just that if you go by your suggestion, the AlertUtility object gets released before the didDismissWithButtonIndex message is sent to it. – Mihai Fonoage Jul 07 '10 at 14:00
  • No, it will not. Because in displayAlert that instance gets a retain - which is balanced with the release in didDismiss... – Eiko Jul 07 '10 at 14:13
  • I did not see the retain, I'm sorry. It works with the retain, you are right. – Mihai Fonoage Jul 07 '10 at 14:22
  • I down voted because you never call `[self release]`period. `[AlertUtility alertView:didDismissWithButtonIndex:]` should not be sending a `release` to self==`AlertUtility`. However, you could use `[alertView release]` just fine. – TechZen Jul 07 '10 at 14:42
  • You could release alertView there instead of in didDismiss... but this is not the point here. The point is, that if you don't [retain self] in displayAlert then how could you ever make sure that you won't operate on a stale object in didDidmiss...? You can't! That's why you need to retain it there, and correctly release it later. You need this, because the delegate doesn't retain. – Eiko Jul 07 '10 at 15:01
0

You can use this instead:

void displayAlert(NSString *title, NSString *message)
{
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
        message:message delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [alert show];
    [alert release];
}

Or just:

#define MY_ALERT(TITLE, MESSAGE) \
[[[[UIAlertView alloc] initWithTitle:TITLE \
                             message:MESSAGE \
                            delegate:nil \
                   cancelButtonTitle:@"OK" \
                   otherButtonTitles:nil] autorelease] show]

And then write:

MY_ALERT(nil, @"Press, OK!");
nepo
  • 64
  • 3
  • Thanks for your feedback. I don't have a problem with releasing the UIAlertView object, but rather with releasing the class that encapsulates the method where I create the alert, namely AlertUtility. And I can't set the delegate to nil since I want to be notified when a button on the alert has been pressed. – Mihai Fonoage Jul 07 '10 at 13:31