强烈提到“自我”以保持对象存活(暂时):邪恶?

时间:2015-02-09 20:11:02

标签: ios objective-c automatic-ref-counting uialertview object-lifetime

我正在为UIAlertView创建一个包装器(我知道UIAlertController和几个已经存在的包装器,它也用于教育目的)。

假设它看起来像这样(非常缩短的版本):

@interface MYAlertView : NSObject
-(void)show;
@end

@interface MYAlertView()<UIAlertViewDelegate>
@end

@implementation MYAlertView
-(void)show {
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Some title"
                                                        message:@"Some message"
                                                       delegate:self 
                                              cancelButtonTitle:@"Cancel"
                                              otherButtonTitles:nil];

    [alertView show]
}

#pragma mark UIAlertViewDelegate implementation

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    //Do something.
}
@end

而且,例如,我这样使用它:

// USAGE (inside some ViewController)
-(void)showMyAlert {
    dispatch_async(dispatch_get_main_queue(), ^{
        MYAlertView *myAlertView = [[MYAlertView alloc] init];
        [myAlertView show];
    });
}

我遇到的问题如下:

  1. [myAlertView show]会导致alertView出现。 myAlertView被设置为alertView
  2. 的代理人
  3. myAlertView方法中只有showMyAlert的强引用:在myAlertView方法的块内。完成后,alertView将被取消分配。
  4. 当用户单击alertView上的按钮时,myAlertView会调用它的委托方法,但委托(UIAlertView)已经解除分配,因此会导致BAD_ACCESS(委托人) assign被声明为weak,而非MYAlertView)。
  5. 我希望UIAlertViewmyAlertView一样易于使用,所以我不想让用户在某处存储强引用(这很不方便)。< / p>

    所以,只要alertView以某种方式显示,我就必须保持MyAlertView活着。问题是,除了在self内创建强引用,将其分配给alertView,当我显示nil并将其分配给{{1}时,我无法想到任何其他方式当我解雇它时。

    像这样(只改变了位):

    @interface MYAlertView()<UIAlertViewDelegate>
    //ADDED:
    @property (nonatomic, strong) id strongSelfReference;
    @end
    
    @implementation MYAlertView
    -(void)show {
        UIAlertView *alertView = [[UIAlertView alloc] init /*shortened*/];
        [alertView show]
    
        //ADDED:
        self.strongSelfReference = self;
    }
    
    #pragma mark UIAlertViewDelegate implementation
    //ADDED:
    - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
        self.strongSelfReference = nil;
    }
    @end
    

    它应该有效:alertView被解除的那一刻,strongSelfReference将被设置为nilmyAlertView将没有强大的引用,并且它将被解除分配(理论上)。

    但是像这样强烈提及self对我来说是邪恶的。还有更好的方法吗?

    更新: MYAlertView实际上是now deprecated UIAlertView和新UIAlertController(iOS 8+)周围的抽象层,所以子类化UIAlertView不是一个选项。

3 个答案:

答案 0 :(得分:9)

是的,你的对象应该对自己有很强的参考价值。这样做并不邪恶。

自我引用(或者,通常,任何参考周期)本身并不是邪恶的。邪恶来自于无意中创造一个它永远不会被打破,从而泄漏物体。你不是那样做的。

答案 1 :(得分:1)

我觉得这里的答案是实际将MYAlertView实现为UIAlertView子类而不是浮动在以太网中的对象。只要您的内部UIAlertView经常陷入困境,它就会一直存在。

@interface MYAlertView : UIAlertView
@end

@implementation MYAlertView
- (instancetype)init {
  if (self = [super initWithTitle:@"Some title"
                          message:@"Some message"
                         delegate:self 
                cancelButtonTitle:@"Cancel"
                otherButtonTitles:nil]) {
    // Other setup?
  }
  return self;
}

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
  // Respond.
}
@end

更新:您应该为UIAlertViewController创建一个iOS7类比。

@interface MyAlertViewController : UIViewController <UIAlertViewDelegate>
+ (id)makeMeOne;
@end

@implementation MyAlertViewController
- (void)viewDidAppear:(BOOL)animated {
  UIAlertView *alert = [[UIAlertView alloc] init..];
  [alert show];
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
  // Respond.
  [self.navigationController popViewControllerAnimated:NO];
}
+ (id)makeMeOne {
  if (iOS7) {
    return [[self alloc] init];
  } else {
    return [[UIAlertViewController alloc] init];
  }
}
@end

填写空白以进行设置。

答案 2 :(得分:0)

在我看来,这是一个糟糕设计的指标。

如果您要为两个iOS版本创建一个包装器,那么最好公开一个反映相关解雇操作的MYAlertView代理(否则您将无法对回调进行操作,而不是这个包装器。)

如果你保持对自己的强引用而不向你要包装的类添加任何实际值,那么编写一个在完成时接受一个块来回调的包装器会更好吗?至少通过这种方式,您可以监控用户的操作,并让呼叫者指示警报的相关时间。

毕竟,如果你传递委托,那么问题就会以可读的方式解决?