xcode – 使用performSelector执行的方法中的objc_retain中的崩溃

前端之家收集整理的这篇文章主要介绍了xcode – 使用performSelector执行的方法中的objc_retain中的崩溃前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有这个奇怪的崩溃与ARC自动插入objc_retains在我的代码.

我有以下两个类:

  1. @interface MenuItem : NSObject
  2. @property (weak,nonatomic) id target;
  3. @property (unsafe_unretained,nonatomic) SEL action;
  4. @property (strong,nonatomic) id object;
  5. - (instancetype)initWIthTarget:(id)target action:(SEL)action withObject:(id)object;
  6. - (void)performAction;
  7. @end
  8.  
  9. @implementation MenuItem
  10. - (void)performAction
  11. {
  12. if (self.target && self.action)
  13. {
  14. if (self.object)
  15. {
  16. [self.target performSelector:self.action withObject:self.object];
  17. }
  18. else
  19. {
  20. [self.target performSelector:self.action];
  21. }
  22. }
  23. }
  24. @end
  25.  
  26. @interface Widget : NSObject
  27. - (void)someMethod:(id)sender;
  28. @end

在某些时候,我实例化一个MenuItem:

  1. MenuItem *item = [MenuItem alloc] initWithTarget:widget action:@selector(someMethod:) object:nil];

然后在其他地方,我在菜单项上调用performAction:

  1. [item performAction];

在执行某些方法我遇到崩溃:

  1. @implementation Widget
  2. - (void)someMethod:(id)sender
  3. {
  4. // EXEC_BAD_ACCESS crash in objc_retain
  5. }
  6. @end

为什么会这样?

解决方法

崩溃的原因是因为我使用了错误的performSelector.

NSObject定义了多个版本的performSelector.我正在援引的是:

  1. - (id)performSelector:(SEL)aSelector;

但是,我正在调用方法使用了一个id参数.例如:

  1. - (void)someMethod:(id)sender;

现在,ARC是一个很好的安全内存管理系统,它正在尝试确保在执行方法期间适当地保留参数.所以即使我的someMethod:是空的ARC生产的代码看起来像这样:

  1. - (void)someMethod:(id)sender
  2. {
  3. objc_retain(sender);
  4. objc_release(sender);
  5. }

然而,这个问题是我正在调用performSelector:而不是为sender参数提供一个值.所以发件人指着堆栈上的随机垃圾.因此,当调用objc_retain()时,应用程序崩溃.

如果我改变:

  1. MenuItem *item = [[MenuItem alloc] initWithTarget:widget
  2. action:@selector(someMethod:)
  3. object:nil];

  1. MenuItem *item = [[MenuItem alloc] initWithTarget:widget
  2. action:@selector(someMethod)
  3. object:nil];

  1. - (void)someMethod:(id)sender;

  1. - (void)someMethod;

然后崩溃消失了.

同样,我也可以改变

  1. [self.target performSelector:self.action];

  1. [self.target performSelector:self.action withObject:nil];

如果我想遵循采用单个参数的“标准”形式的目标动作方法. performSelector的第二种形式的好处是,如果我正在调用一个不接受参数的方法,它仍然可以正常工作.

猜你在找的iOS相关文章