对 `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];
});
}
我遇到的问题如下:
[myAlertView show]
导致 alertView
出现。 myAlertView
被设置为 alertView
的代表。
- 在
showMyAlert
方法的块内只有对 myAlertView
: 的强引用。完成后,myAlertView
被释放。
- 当用户单击
alertView
上的按钮时,alertView
调用它的委托方法,但是委托 (myAlertView
) 已经被释放,所以它导致 BAD_ACCESS(UIAlertView
中的委托被声明为 assign
,而不是 weak
)。
我想让 MYAlertView
像 UIAlertView
一样易于使用,所以我不想让用户在某处存储对它的强引用(这很不方便) .
所以,只要 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
将被设置为 nil
,myAlertView
将没有强引用,它将被释放(理论上)。
但像这样保持对 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 添加任何实际值,也许最好编写一个包装器来接受块以在完成时回调?至少通过这种方式,您可以监控用户的操作并让调用者决定警报的相关时间。
毕竟,如果您传递委托,那么问题就以可读的方式解决了?
我正在为 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];
});
}
我遇到的问题如下:
[myAlertView show]
导致alertView
出现。myAlertView
被设置为alertView
的代表。- 在
showMyAlert
方法的块内只有对myAlertView
: 的强引用。完成后,myAlertView
被释放。 - 当用户单击
alertView
上的按钮时,alertView
调用它的委托方法,但是委托 (myAlertView
) 已经被释放,所以它导致 BAD_ACCESS(UIAlertView
中的委托被声明为assign
,而不是weak
)。
我想让 MYAlertView
像 UIAlertView
一样易于使用,所以我不想让用户在某处存储对它的强引用(这很不方便) .
所以,只要 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
将被设置为 nil
,myAlertView
将没有强引用,它将被释放(理论上)。
但像这样保持对 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
.
@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 添加任何实际值,也许最好编写一个包装器来接受块以在完成时回调?至少通过这种方式,您可以监控用户的操作并让调用者决定警报的相关时间。
毕竟,如果您传递委托,那么问题就以可读的方式解决了?