【问题标题】:Crash in objc_retain in method performed with performSelector使用 performSelector 执行的方法中的 objc_retain 崩溃
【发布时间】:2012-08-09 02:20:44
【问题描述】:

我的代码中出现了与 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];

someMethod 的执行中我遇到了崩溃:

@implementation Widget
- (void)someMethod:(id)sender
{
  // EXEC_BAD_ACCESS crash in objc_retain
}
@end

为什么会这样?

【问题讨论】:

    标签: xcode automatic-ref-counting performselector


    【解决方案1】:

    崩溃的原因是我使用了错误的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:@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 的第二种形式的好处是,如果我调用一个不带参数的方法,它仍然可以正常工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-07
      • 1970-01-01
      • 1970-01-01
      • 2011-07-01
      • 1970-01-01
      • 2015-10-11
      • 1970-01-01
      • 2012-03-30
      相关资源
      最近更新 更多