我已经了解了如何为UIAlertView提供上下文的一些想法。常见的答案是将其保存在字典或子类UIAlertView中。我不喜欢在字典中保存上下文的想法,这是数据的错误位置。 Apple不支持对UIAlertView进行子类化,因此根据我的标准,这不是一个好的解决方案。
我提出了一个想法,但我不知道该怎么做。创建一个上下文对象的实例,该对象是UIAlertView的委托。反过来,警报视图上下文有自己的委托,它是视图控制器。
麻烦在于释放记忆。我将alertView.delegate设置为nil并调用[self autorelease]来释放-alertView中的上下文对象:didDismissWithButtonIndex:。
问题:我自己会遇到什么问题?我怀疑自己正在为自己的内存错误做好准备。
以下是仅支持-alertView的简单版本:clickedButtonAtIndex:
- (void)askUserIfTheyWantToSeeRemoteNotification:(NSDictionary *)userInfo
{
[[[[UIAlertView alloc] initWithTitle:[userInfo valueForKey:@"action"]
message:[userInfo valueForKeyPath:@"aps.alert"]
delegate:[[WantAlertViewContext alloc] initWithDelegate:self context:userInfo]
cancelButtonTitle:@"Dismiss"
otherButtonTitles:@"View", nil] autorelease] show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex withContext:(id)context
{
if (buttonIndex != alertView.cancelButtonIndex)
[self presentViewForRemoteNotification:context];
}
@protocol WantAlertViewContextDelegate <NSObject>
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex withContext:(id)context;
@end
@interface WantAlertViewContext : NSObject <UIAlertViewDelegate>
- (id)initWithDelegate:(id<WantAlertViewContextDelegate>)delegate context:(id)context;
@property (assign, nonatomic) id<WantAlertViewContextDelegate> delegate;
@property (retain, nonatomic) id context;
@end
@implementation WantAlertViewContext
- (id)initWithDelegate:(id<WantAlertViewContextDelegate>)delegate context:(id)context
{
self = [super init];
if (self) {
_delegate = delegate;
_context = [context retain];
}
return self;
}
- (void)dealloc
{
[_context release];
[super dealloc];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
[self.delegate alertView:alertView clickedButtonAtIndex:buttonIndex withContext:self.context];
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
alertView.delegate = nil;
[self autorelease];
}
@synthesize delegate = _delegate;
@synthesize context = _context;
@end
答案 0 :(得分:5)
您可以使用关联对象的概念。使用函数objc_setAssociatedObject()
和objc_getAssociatedObject()
。您可以使用这些属性实际上通过类别将新属性(在您的情况下为NSDictionary
)添加到对象。
以下是UIAlertView
类别的示例。如果项目使用ARC,则应在不设置ARC
,-fno-objc-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
此类别很容易使用。
使用ARC的 SomeViewController.m: a UIAlertViewDelegate
。
-(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);
}
当你按下alertview的OK按钮时,你会看到:
userInfo:{
Greeting = Hello;
}
几点说明:
1)确保关联类型与属性声明匹配,以便事物按预期运行。
2)您可能不应该使用userInfo
作为属性/关联,因为Apple可能会决定在将来向userInfo
添加UIAlertView
属性。
修改 解决您对[self autorelease];
您必须平衡隐含的alloc
保留此行:delegate:[[WantAlertViewContext alloc] initWithDelegate:self context:userInfo]
。您可以通过在最终[self autorelease];
委托方法中调用UIAlertView
来实现此平衡。
当然,这确实是错的。主要是因为在看这个时没有办法,它一开始就不会看起来像内存管理错误。但有一种简单的方法可以避免您正在创建的“受控泄漏”API;让WantAlertViewContext
的实例明确保留自己。例如:
-(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
}
现在你的班级有一些内在的和谐。我说一些,因为这仍然不完美。例如,如果实例永远不是警报视图委托,则永远不会释放它。它仍然只是一个“半控制”的内存泄漏。
无论如何,现在你的实例化调用看起来更合乎逻辑了:
delegate:[[[WantAlertViewContext alloc] initWithDelegate:self context:userInfo] autorelease];
我认为这种特殊的设计模式充满了危险。如果你最终使用它,请密切关注它。
答案 1 :(得分:0)
我想出了一个更简单的解决方案,可能适合某些情况。因为在调用委托时获得NSAlertView上下文,所以我使用对象的实际地址来创建标记(NSString *),然后我将其用于在全局或特定于对象的NSDictionary中存储自定义值。这是一个例子:
+(NSString*)GetTag:(id)ObjectIn
{
return [NSString stringWithFormat:@"Tag-%i",(int)ObjectIn];
}
在代表中:
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString* MyID = [CommandManager GetTag:alertView];
[CurrentActiveAlerts removeObjectForKey:MyID];
}
通话:
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];
键将最终看起来像“Tag-226811776”。希望这会有所帮助。