对 `self` 的强引用以使对象保持活动状态(暂时):邪恶?

Strong reference to `self` to keep the object alive (temporarily): evil?

我正在为 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. showMyAlert 方法的块内只有对 myAlertView: 的强引用。完成后,myAlertView 被释放。
  3. 当用户单击 alertView 上的按钮时,alertView 调用它的委托方法,但是委托 (myAlertView) 已经被释放,所以它导致 BAD_ACCESS(UIAlertView 中的委托被声明为 assign,而不是 weak)。

我想让 MYAlertViewUIAlertView 一样易于使用,所以我不想让用户在某处存储对它的强引用(这很不方便) .

所以,只要 alertView 以某种方式显示,我就必须让 myAlertView 保持活动状态。问题是除了在 MyAlertView 中创建一个强引用,当我显示 alertView 时将其分配给 self 并将其分配给 [=35] 之外,我想不出任何其他方法=],当我解雇它时。

像这样(只有改变的位):

@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 不是一个选项。

我觉得这里的答案是实际实现 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

填写空白以进行设置。

是的,您的对象应该保持对自身的强引用。这样做并不邪恶。

自引用(或者,一般来说,任何引用循环)本质上并不是邪恶的。恶者无意中造出一个永不破损的东西,从而漏出物件。你没有那样做。

在我看来,这是糟糕设计的标志。

如果您正在为两个 iOS 版本创建一个包装器,您最好为 MYAlertView 公开一个反映相关关闭操作的委托(否则您将无法执行在回调上,比这个包装器更远)。

如果你保持对自己的强引用而不向你正在包装的 class 添加任何实际值,也许最好编写一个包装器来接受块以在完成时回调?至少通过这种方式,您可以监控用户的操作并让调用者决定警报的相关时间。

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