0

我已经在ARC下创建了一个类,其中有一些方法接受块。问题是应用程序不断崩溃,我认为崩溃的原因是对象正在由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立即被调用(在执行block之前)。它不会崩溃,如果我创建一个magicalRecord的属性并使用它,但根据我的要求,我不想使用全局iVar或属性只是调用此代码块。

有什么建议吗?

回答

1

问题是你的KelaMagicalControl在showMagicalControl:方法的末尾被释放,它不会被保留在任何地方。只有你在showWithTouchCompletionBlock中创建的UIView:会被保留,因为你将它添加到了超级视图,在这个例子中是窗口。这就是为什么弹出窗口显示正确。但是一个目标总是不安全的,所以当你点击那个视图时,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的你重新显示,它会自动保留,因为它有一个超级视图。当您点击视图时,完成模块现在可以随意执行。确保在完成块结束时将其从超级视图中移除,否则它将永远不会被释放。

+0

谢谢。这有助于很多,并按预期工作:)。我希望我能投票,但我没有足够的声誉。我感谢您的帮助。 – Kela 2013-05-05 19:06:00

0

你是对的,magicalControl被释放,因为它结束了他的范围。我没有测试以下内容,但它应该可以工作。

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

在block中声明一个强引用会保留magicalControl。

+0

“但是为了避免保留cicle,那么在块完成时你必须将其删除。”你没有零吗? – newacct 2013-05-06 06:15:00

+0

是的,是行“retainVar = nil;” – Ultrakorne 2013-05-06 07:49:03

+0

@Ultrakome:'retainedVar'是一个局部变量。将它设置在块的末尾是完全没用的。 – newacct 2013-05-06 23:29:11

0

一种解决方案是使用UIGestureRecognizer的块API(有许多版本在Internet上浮动),然后在该块中调用[self mainViewTapped]。这会保留您的KelaMagicalControl,并确保KelaMagicalControl只要手势识别器可以调用它即可使用。