我有这个奇怪的崩溃与ARC自动插入objc_retains在我的代码.
我有以下两个类:
@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
在某些时候,我实例化一个MenuItem:
MenuItem *item = [MenuItem alloc] initWithTarget:widget action:@selector(someMethod:) object:nil];
然后在其他地方,我在菜单项上调用performAction:
[item performAction];
在执行某些方法我遇到崩溃:
@implementation Widget - (void)someMethod:(id)sender { // EXEC_BAD_ACCESS crash in objc_retain } @end
为什么会这样?
解决方法
崩溃的原因是因为我使用了错误的performSelector.
NSObject定义了多个版本的performSelector.我正在援引的是:
- (id)performSelector:(SEL)aSelector;
- (void)someMethod:(id)sender;
现在,ARC是一个很好的安全内存管理系统,它正在尝试确保在执行方法期间适当地保留参数.所以即使我的someMethod:是空的ARC生产的代码看起来像这样:
- (void)someMethod:(id)sender { objc_retain(sender); objc_release(sender); }
然而,这个问题是我正在调用performSelector:而不是为sender参数提供一个值.所以发件人指着堆栈上的随机垃圾.因此,当调用objc_retain()时,应用程序崩溃.
如果我改变:
MenuItem *item = [[MenuItem alloc] initWithTarget:widget action:@selector(someMethod:) object:nil];
至
MenuItem *item = [[MenuItem alloc] initWithTarget:widget action:@selector(someMethod) object:nil];
和
- (void)someMethod:(id)sender;
至
- (void)someMethod;
然后崩溃消失了.
同样,我也可以改变
[self.target performSelector:self.action];
至
[self.target performSelector:self.action withObject:nil];
如果我想遵循采用单个参数的“标准”形式的目标动作方法. performSelector的第二种形式的好处是,如果我正在调用一个不接受参数的方法,它仍然可以正常工作.