我在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被立即调用(在执行块之前)。如果我创建了magicalRecord的属性并使用它,它不会崩溃,但根据我的要求,我不想使用全局iVar或属性来调用这个块代码。
有什么建议吗?
答案 0 :(得分: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,因此它将自动保留,因为它具有超级视图。点击视图后,现在可以根据需要执行完成块。确保在完成块的末尾将其从超级视图中删除,否则它将永远不会被释放。
答案 1 :(得分:0)
你是对的,magicalControl会被取消分配,因为它结束了他的范围。我没有测试过以下但它应该可以工作。
KelaMagicalControl * magicalControl = [[KelaMagicalControl alloc] initWithTitle:title message:message];
[magicalControl showWithTouchCompletionBlock:^{
KelaMagicalControl *retainedVar = magicalControl;
NSLog(@"control tapped");
}];
在块内声明一个强引用将保留magicalControl。
答案 2 :(得分:0)
一种解决方案是使用UIGestureRecognizer
的块API(互联网上有许多版本的这种版本),然后在块中调用[self mainViewTapped]
。这会保留您的KelaMagicalControl
,并且只要手势识别器可以调用它,就会确保KelaMagicalControl
可用。