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只要手勢識別器可以調用它即可使用。