在使用performSelector执行的方法中发生了objc_retain崩溃。
在使用performSelector执行的方法中发生了objc_retain崩溃。
在我的代码中,与ARC自动插入objc_retains相关的奇怪崩溃问题。我有以下两个类:\n
@interface MenuItem : NSObject @property (weak, nonatomic) id target; @property (unsafe_unretained, nonatomic) SEL action; @property (strong, nonatomic) id object; - (instancetype)initWIthTarget:(id)target action:(SEL)action withObject:(id)object; - (void)performAction; @end @implementation MenuItem - (void)performAction { if (self.target && self.action) { if (self.object) { [self.target performSelector:self.action withObject:self.object]; } else { [self.target performSelector:self.action]; } } } @end @interface Widget : NSObject - (void)someMethod:(id)sender; @end
\n在某个地方,我像这样实例化了一个MenuItem:\n
MenuItem *item = [MenuItem alloc] initWithTarget:widget action:@selector(someMethod:) object:nil];
\n然后在其他地方,我调用了菜单项的performAction方法:\n
[item performAction];
\n在someMethod的实现中,我遇到了崩溃:\n
@implementation Widget - (void)someMethod:(id)sender { // 在objc_retain中出现EXEC_BAD_ACCESS崩溃 } @end
\n为什么会出现这种情况?
在这个问题中,崩溃的原因是因为我使用了错误的performSelector
方法。
NSObject
定义了多个版本的performSelector
方法。我调用的是:
- (id)performSelector:(SEL)aSelector;
然而,我调用的方法需要一个id
参数,例如:
- (void)someMethod:(id)sender;
现在,由于ARC是一个安全的内存管理系统,它试图确保在方法执行过程中参数被正确地保留。因此,即使我的someMethod:
是空的,ARC生成的代码看起来像这样:
- (void)someMethod:(id)sender { objc_retain(sender); objc_release(sender); }
然而,问题在于我调用了performSelector:
而没有为sender
参数提供值。因此,sender
指向堆栈上的随机垃圾数据。因此,当调用objc_retain()
时,应用程序崩溃了。
如果我将:
MenuItem *item = [[MenuItem alloc] initWithTarget:widget action:(someMethod:) object:nil];
改为:
MenuItem *item = [[MenuItem alloc] initWithTarget:widget action:(someMethod) object:nil];
和:
- (void)someMethod:(id)sender;
改为:
- (void)someMethod;
那么崩溃就消失了。
同样地,如果我想遵循接受单个参数的标准target-action方法的形式,我还可以将:
[self.target performSelector:self.action];
改为:
[self.target performSelector:self.action withObject:nil];
第二种形式的performSelector
的好处是,如果我调用的方法不需要参数,它仍然可以正常工作。