How do UIAlertView or UIActionSheet handle retaini

2019-08-05 10:36发布

I want to create a similar class to UIAlertView which doesn't require a strong ivar.

For example, with UIAlertView, I can do the following in one of my UIViewController's methods:

UIAlertView *alertView     = [[UIActionSheet alloc] initWithTitle:nil
                                                          message:@"Foo"
                                                         delegate:nil
                                                cancelButtonTitle:@"Cancel"
                                                otherButtonTitles:nil];
[alertView show];

... and the actionSheet will not be dealloced until it is no longer visible.

If I were to try to do the same thing:

MYAlertView *myAlertView = [[MYAlertView alloc] initWithMessage:@"Foo"];
[myAlertView show];

... the myAlertView instance will automatically be dealloced at the end of the current method I am in (e.g. right after the [myAlertView show] line).

What is the proper way to prevent this from happening without having to declare myView as a strong property on my UIViewController? (I.e. I want myView to be a local variable, not an instance variable, and I would like the MYAlertView instance to be in charge of its own lifecycle rather than my UIViewController controlling its lifecycle.)

Update: MYAlertView inherits from NSObject, so it cannot be added to the Views hierarchy.

4条回答
狗以群分
2楼-- · 2019-08-05 11:05

You need to retain it somehow until it is released.

I do not really understand why you cannot implement it as subclass of UIView. Then you could use the view hierarchy as the keeper of a strong reference (retain +1). But you will have good reasons for not doing so.

If you don't have such a thing then I would use an NSMutableArray as class varialbe (meaning statc). Just declare it in the @interface block and initialize it with nil:

@interface

static NSMutableArray _allMyAlerts = nil;

provide an accessor.

-(NSMutableArray *) allMyAlerts {

  if (_allMyAlerts == nil) {
     _allMyAlerts = [[NSMutableArray alloc] init];
  }
  return _allMyAlerts
}

Within the init method do the following:

- (id) init {

  self = [super init];
  if (self) {
    [[self allMyAlerts] addObject:self];
  }

}

You will invode some method when the alert is dismissed.

- (void) dismissAlert {

  // Do your stuff here an then remove it from the array. 

  [[self allMyAlerts] removeObject:self];
}

You may want to add some stuff to make it mutli threading save, which it is not. I just want to give an example that explains the concept.

allMyAlert could be an NSMutableSet as well. No need for an array as far as I can see. Adding the object to an array or set will add 1 to the retain count and removing it will reduce it by 1.

查看更多
啃猪蹄的小仙女
3楼-- · 2019-08-05 11:14

I've done my own AlertView with a little trick.

Just retain the object himself and release it on action. With this, you can call your custom alert vies as native one.

#import "BubbleAlertView.h"
#import <QuartzCore/QuartzCore.h>

@interface BubbleAlertView ()
...
@property (nonatomic, strong) BubbleAlertView *alertView;
...

@end

@implementation BubbleAlertView

...

- (id)initWithTitle:(NSString*)title message:(NSString*)message delegate:(id)delegate cancelButtonTitle:(NSString*)cancelButtonTitle okButtonTitle:(NSString*) okButtonTitle
{
self = [super init];
if (self)
{
    // Custom initialization
    self.alertView = self; // retain myself

    //More init stuff 
}
return self;
}
...

//SHOW METHOD

- (void)show
{
// We need to add it to the window, which we can get from the delegate
id appDelegate = [[UIApplication sharedApplication] delegate];
UIWindow *window = [appDelegate window];
[window addSubview:self.view];

// Make sure the alert covers the whole window
self.view.frame = window.frame;
self.view.center = window.center;

}

- (IBAction)btPressed:(id)sender
{
//Actions done

[UIView animateWithDuration:0.4f animations:^{
    self.vContent.alpha = 0.f;
} completion:^(BOOL finished) {
    [self.view removeFromSuperview];
    self.alertView = nil; // deallocate myself
}];

}
查看更多
在下西门庆
4楼-- · 2019-08-05 11:16

If you add it as a subview of another view it will be retained. When the user selects and action or dismisses it, then it should call self removeFromSuperview as it's last act.

查看更多
叛逆
5楼-- · 2019-08-05 11:27

UIAlertView creates a UIWindow, which it retains. The alert view then adds itself as a subview of the window, so the window retains the alert view. Thus it creates a retain cycle which keeps both it and its window alive. UIActionSheet works the same way.

If you need your object to stay around, and nothing else will retain it, it's fine for it to retain itself. You need to make sure you have a well-defined way to make it release itself when it's no longer needed. For example, if it's managing a window, then it should release itself when it takes the window off the screen.

查看更多
登录 后发表回答