我有这个奇怪的崩溃与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的第二种形式的好处是,如果我正在调用一个不接受参数的方法,它仍然可以正常工作.