0

ブロックを受け入れるいくつかのメソッドを使用して、ARC の下にクラスを作成しました。問題はアプリがクラッシュし続けることです。クラッシュの理由は、オブジェクトが ARC によって解放されているためだと思います。私の質問は、どうすればこれを修正できるか、つまり、オブジェクトの参照を保持して、ブロックが処理されるまでオブジェクトが解放されないようにする方法です。

ここに.hクラスがあります

#if NS_BLOCKS_AVAILABLE
typedef void (^KelaMagicalControlCompletionBlock)();
#endif

@interface KelaMagicalControl : NSObject

+(KelaMagicalControl *)controlWithTitle:(NSString *)title message:(NSString *)message;
-(id)initWithTitle:(NSString *)title message:(NSString *)message;

-(void)showWithTouchCompletionBlock:(KelaMagicalControlCompletionBlock)completionBlock;

@end

ここに.mクラスがあります

#import "KelaMagicalControl.h"

@interface KelaMagicalControl()

@property (nonatomic, strong) NSString * title;
@property (nonatomic, strong) NSString * message;

@property (copy) KelaMagicalControlCompletionBlock completionBlock;

@end

@implementation KelaMagicalControl

-(void)dealloc
{
   NSLog(@"deallocated");
}

+ (KelaMagicalControl *)toastWithTitle:(NSString *)title message:(NSString *)message
{
   KelaMagicalControl * magicalControl = [[KelaMagicalControl alloc] initWithTitle:title   message:message];
   return magicalControl;
}
-(id)initWithTitle:(NSString *)title message:(NSString *)message
{
    if(self = [super init])
    {
        _title = title;
        _message = message;
    }
    return self;
}

-(void)showWithTouchCompletionBlock:(void (^)())completionBlock
{

    UIWindow * mainWindow = [[UIApplication sharedApplication]keyWindow];
    UIView * viewTemp = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 300, 100)];
    [viewTemp setTag:10001];
    [viewTemp setBackgroundColor:[UIColor redColor]];
    [mainWindow addSubview:viewTemp];

    UITapGestureRecognizer * tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(mainViewTapped)];
    [viewTemp addGestureRecognizer:tapGestureRecognizer];

    self.completionBlock = completionBlock;

}

-(void)mainViewTapped
{
    if(self.completionBlock)
    {
        self.completionBlock();
        self.completionBlock = nil;
    }
}

コントローラ クラスから、次のようにカスタム クラスのメソッドにメッセージを送信します。

-(IBAction)showMagicalControl:(id)sender
{
    NSString * title = @"Title";
    NSString * message = @"This is a very long message";


    KelaMagicalControl * magicalControl = [[KelaMagicalControl alloc] initWithTitle:title message:message];
    [magicalControl showWithTouchCompletionBlock:^{
        NSLog(@"control tapped");
    }];
}

したがって、コントロールは正常に表示されますが、ブロックを実行する代わりにタップすると、エラー「obj_msgsend」でクラッシュします。showMagicalControl メソッドにも到達しません。私はARCを使用しているので、自動的に解放されていると思います.deallocがすぐに(ブロックを実行する前に)呼び出されるのを見ることができます. magicRecord のプロパティを作成して使用してもクラッシュしませんが、私の要件では、このブロック コードを呼び出すためだけにグローバル iVar またはプロパティを使用したくありません。

何か提案はありますか?

4

3 に答える 3

1

問題は、KelaMagicalControl が showMagicalControl: メソッドの最後で解放され、どこにも保持されないことです。スーパービュー (この場合はウィンドウ) に追加したため、showWithTouchCompletionBlock: 内で作成した UIView のみが保持されます。そのため、ポップアップが正しく表示されます。しかし、ターゲットは常に unsafe_unretained であるため、そのビューをタップすると、gestureRecognizer は既に解放されている KelaMagicalControl を呼び出そうとするため、クラッシュが発生します。

これは、KelaMagicalControl を UIView のサブクラスにすることで簡単に解決できます。必要な変更をすぐに入力しました。

.h ファイル

#import <UIKit/UIKit.h>

#if NS_BLOCKS_AVAILABLE
typedef void (^KelaMagicalControlCompletionBlock)();
#endif

@interface KelaMagicalControl : UIView
{
    NSString* _title;
    NSString* _message;
}

-(id)initWithTitle:(NSString *)title message:(NSString *)message;
-(void)showWithTouchCompletionBlock:(KelaMagicalControlCompletionBlock)completionBlock;

@end

.m ファイル

#import "KelaMagicalControl.h"

@interface KelaMagicalControl()

@property (nonatomic, strong) NSString * title;
@property (nonatomic, strong) NSString * message;

@property (copy) KelaMagicalControlCompletionBlock completionBlock;

@end

@implementation KelaMagicalControl

-(void)dealloc
{
    NSLog(@"deallocated");
}

+ (KelaMagicalControl *)toastWithTitle:(NSString *)title message:(NSString *)message
{
    KelaMagicalControl * magicalControl = [[KelaMagicalControl alloc] initWithTitle:title   message:message];
    return magicalControl;
}

-(id)initWithTitle:(NSString *)title message:(NSString *)message
{
    self = [super initWithFrame:CGRectMake(10, 10, 300, 300)];
    if (self)
    {
        _title = title;
        _message = message;
    }
    return self;
}

-(void)showWithTouchCompletionBlock:(void (^)())completionBlock
{
    UIWindow * mainWindow = [[UIApplication sharedApplication]keyWindow];
    [self setTag:10001];
    [self setBackgroundColor:[UIColor redColor]];
    [mainWindow addSubview:self];

    UITapGestureRecognizer * tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(mainViewTapped)];
    [self addGestureRecognizer:tapGestureRecognizer];

    self.completionBlock = completionBlock;
}

-(void)mainViewTapped
{
    if(self.completionBlock)
    {
        self.completionBlock();
        self.completionBlock = nil;
    }
}
@end

KelaMagicalControl は現在表示している UIView であるため、スーパービューがあるため自動的に保持されます。ビューをタップすると、完了ブロックが必要に応じて実行されるようになりました。完了ブロックの最後に、スーパービューから削除するようにしてください。そうしないと、決して解放されません。

于 2013-05-05T11:37:08.577 に答える
0

解決策の 1 つは、ブロック API を使用してUIGestureRecognizer(この API の多くのバージョンがインターネットに出回っています)、ブロック内で を呼び出すこと[self mainViewTapped]です。これにより、 が保持KelaMagicalControlKelaMagicalControlれ、ジェスチャ レコグナイザーが呼び出すことができる限り、 が使用できるようになります。

于 2013-05-06T06:19:02.200 に答える
0

そうです、magicalControl の割り当てが解除されるのは、彼のスコープが終了するためです。以下はテストしていませんが、動作するはずです。

KelaMagicalControl * magicalControl = [[KelaMagicalControl alloc] initWithTitle:title message:message];
    [magicalControl showWithTouchCompletionBlock:^{
        KelaMagicalControl *retainedVar = magicalControl;
        NSLog(@"control tapped");
    }];

ブロック内で強い参照を宣言すると、magicalControl が保持されます。

于 2013-05-05T11:21:26.723 に答える