UIAlertView with a user supplied context and [self autorelease]

こ雲淡風輕ζ 提交于 2019-12-04 17:59:19

You can use the concept of associated objects. Using the functions objc_setAssociatedObject() and objc_getAssociatedObject(). You can use these properties to essentially add a new property, in your case to hold an NSDictionary, to an object through a category.

Here is an example of a UIAlertView category. These files should be compiled without ARC, -fno-objc-arc flag set if the project is using ARC.

UIAlertView+WithContext.h:

#import <UIKit/UIKit.h>
@interface UIAlertView (Context)
@property (nonatomic, copy) NSDictionary *userInfo;
@end

UIAlertView+WithContext.m:

#import "UIAlertView+WithContext.h"
// This enum is actually declared elseware
enum {
    OBJC_ASSOCIATION_ASSIGN = 0,
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
    OBJC_ASSOCIATION_RETAIN = 01401,
    OBJC_ASSOCIATION_COPY = 01403
};
@implementation UIAlertView (Context) 
static char ContextPrivateKey;
-(void)setUserInfo:(NSDictionary *)userInfo{
    objc_setAssociatedObject(self, &ContextPrivateKey, userInfo, 3);
}
-(NSDictionary *)userInfo{
    return objc_getAssociatedObject(self, &ContextPrivateKey);
}
@end

This category is easily used.

SomeViewController.m: a UIAlertViewDelegate using ARC or not.

-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Title" message:@"Message" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
    alert.userInfo = [NSDictionary dictionaryWithObject:@"Hello" forKey:@"Greeting"];// autorelease if MRC
    [alert show]; // release if MRC
}

-(void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex{
    NSLog(@"userInfo:%@",alertView.userInfo);
}

When you press the alertview's OK button you will see:

userInfo:{
    Greeting = Hello;
}

A couple of notes:

1) Make sure the association type matches the property declaration so things behave as expected.

2) You probably shouldn't use userInfo for the property/association since Apple may well decide to add a userInfo property to UIAlertView in the future.

Edit To address your concerns about your [self autorelease];

It is imperative that you balance your implicit alloc retain from this line: delegate:[[WantAlertViewContext alloc] initWithDelegate:self context:userInfo]. You achieve this balance by calling [self autorelease]; in the final UIAlertView delegate method.

Granted, this does feel wrong. Mostly because there is no way when looking at this that it doesn't at first blush look like memory mis-management. But there is one simple way to avoid this "controlled leak" API you are creating; Have the instance of WantAlertViewContext explicitly retain itself. For example:

-(id)initWithDelegate:(id<WantAlertViewContextDelegate>)delegate context:(id)context{
    self = [super init];
    if (self) {
        _delegate = delegate;
        _context  = [context retain];
    }
    return [self retain]; // Explicitly retain self
}

-(void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex{
    alertView.delegate = nil;
    [self autorelease]; // Or just [self release]; doesn't make much difference at this point
}

Now your class has some internal harmony. I say some because this is still not perfect. For example, if an instance is never an alert-view delegate it will never be released. It is still just a "semi-controlled" memory leak.

Anyway, now your instantiation call can look more logical:

delegate:[[[WantAlertViewContext alloc] initWithDelegate:self context:userInfo] autorelease];

I think that this particular design pattern is fraught with danger. If you do end up using it keep a close eye on it.

I've come up with a simpler solution that may fit in some circumstances. Because you get the NSAlertView context when the delegate gets called, I use the actual address of the object to make a tag (NSString*) which I then use to store custom values in a global or object specific NSDictionary. Here is an example:

+(NSString*)GetTag:(id)ObjectIn
{
    return [NSString stringWithFormat:@"Tag-%i",(int)ObjectIn];
}

In the Delegate:

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    NSString* MyID = [CommandManager GetTag:alertView];
    [CurrentActiveAlerts removeObjectForKey:MyID];
}

Calling:

UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle:title_text
                                                          message:@""
                                                     delegate:self
                                            cancelButtonTitle:nil
                                        otherButtonTitles:button_text ,nil];

    CurrentActiveAlerts[[CommandManager GetTag:myAlert]] = CommandToRun;        // Querky way to link NSDict to UIAlert, but the best I could think of
    [myAlert show];
    [myAlert release];

The keys will end up looking like "Tag-226811776". Hope this helps.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!