【问题标题】:Keep object reference of local variable in ARC在 ARC 中保留局部变量的对象引用
【发布时间】:2013-04-29 07:43:36
【问题描述】:

我在 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 或属性来调用这个块代码。

有什么建议吗?

【问题讨论】:

    标签: ios objective-c ios5 automatic-ref-counting objective-c-blocks


    【解决方案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,它会自动保留,因为它有一个超级视图。当您点击视图时,完成块现在将按照您的意愿执行。确保在完成块的末尾将其从其父视图中删除,否则它将永远不会被释放。

    【讨论】:

    • 谢谢。这有很大帮助并且可以按预期工作:)。我希望我能投票,但我没有足够的声誉。感谢您的帮助。
    【解决方案2】:

    一种解决方案是为UIGestureRecognizer 使用block API(互联网上有很多版本),然后在block 中调用[self mainViewTapped]。这将保留您的 KelaMagicalControl,并确保只要手势识别器调用它,KelaMagicalControl 就可用。

    【讨论】:

      【解决方案3】:

      你是对的,magicControl 被释放是因为它结束了他的作用域。我尚未测试以下内容,但它应该可以工作。

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

      在块内声明强引用将保留magicControl。

      【讨论】:

      • "但是为了避免保留 cicle,你必须在块完成时将它设为 nil。"你把它归零了吗?
      • @Ultrakome:retainedVar 是一个局部变量。将其设置在块的末尾是完全没用的。
      • 是的。无论如何,保持对magicControl的引用应该可以解决问题。我将删除最后一行无用的
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-25
      • 1970-01-01
      • 2014-03-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多