2

UIAlertView にコンテキストを提供する方法についていくつかのアイデアを見てきました。一般的な答えは、辞書またはサブクラスの UIAlertView に保存することです。コンテキストを辞書に保存するという考えは好きではありません。データの場所が間違っています。UIAlertView のサブクラス化は Apple ではサポートされていないため、私の基準では、良い解決策ではありません。

アイデアは思いつきましたが、どうすればよいかわかりません。UIAlertView のデリゲートであるコンテキスト オブジェクトのインスタンスを作成します。アラート ビュー コンテキストには、View Controller である独自のデリゲートがあります。

問題はメモリの解放です。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
4

2 に答える 2

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

このカテゴリは簡単に使用できます。

SomeViewController.m: a UIAlertViewDelegateARC を使用するかどうか。

-(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) 関連付けタイプがプロパティ宣言と一致していることを確認して、期待どおりに動作するようにします。

userInfo2) Apple が将来userInfoプロパティを追加することを決定する可能性があるため、おそらくプロパティ/関連付けには使用しないでくださいUIAlertView

編集 あなたの懸念に対処するには[self autorelease];

allocこの行からの暗黙の保持のバランスをとることが不可欠です: delegate:[[WantAlertViewContext alloc] initWithDelegate:self context:userInfo]. [self autorelease];最後のUIAlertViewデリゲート メソッドを呼び出すことで、このバランスを実現します。

確かに、これは間違っていると感じます。主な理由は、これを見ると、最初はメモリ管理の誤りのように見えないからです。しかし、作成しているこの「制御されたリーク」API を回避する簡単な方法が 1 つあります。インスタンスを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];

この特定の設計パターンには危険が伴うと思います。使用する場合は、注意深く見守ってください。

于 2012-04-25T05:21:04.477 に答える