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



我正在为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]会导致出现alertViewmyAlertView设置为alertView的委托。
  2. 唯一强引用myAlertView:在showMyAlert方法中的块内。完成后,myAlertView被解除分配。
  3. 当用户单击alertView上的按钮时,alertView调用它的委托方法,但委托(myAlertView(已经被释放,所以它会导致BAD_ACCESS(UIAlertView中的委托被声明为assign,而不是weak(。

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

所以,只要以某种方式显示alertView,我就必须保持myAlertView的活力。问题是除了在MyAlertView内部创建一个强大的引用,当我显示alertView时将其分配给self,并在我关闭它时将其分配给nil之外,我想不出任何其他方法。

像这样(只有更改的位(:

@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将被设置为nil,将没有对myAlertView的强引用,并且它将被释放(理论上(。

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

更新:实际上MYAlertView是围绕现已弃用的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公开一个委托(否则您将无法对回调和比此包装器更进一步的操作(。

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

毕竟,如果您通过授权,那么问题就会以可读的方式解决吗?

最新更新