使用performSelector执行的方法中objc_retain崩溃 [英] Crash in objc_retain in method performed with performSelector

查看:798
本文介绍了使用performSelector执行的方法中objc_retain崩溃的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在代码中遇到了与ARC自动插入objc_retains有关的奇怪崩溃.

I have this strange crash relating to ARC auto-inserting objc_retains in my code.

我有以下两个课程:

@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实例化为这样:

At some point I instantiate a MenuItem as such:

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

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

Then elsewhere I invoke performAction on the menu item:

 [item performAction];

someMethod的实现中,我崩溃了:

In the implementation of someMethod I get a crash:

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

为什么会这样?

推荐答案

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

The reason for the crash was because I was using the wrong performSelector.

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

NSObject defines multiple versions of performSelector. The one I was invoking was:

- (id)performSelector:(SEL)aSelector;

但是我调用的方法使用了id参数.例如:

However the method I was invoking took an id parameter. Eg:

- (void)someMethod:(id)sender;

现在ARC是一个很好的安全内存管理系统,它试图确保在执行方法期间正确保留参数.因此,即使我的someMethod:为空,ARC仍会生成如下代码:

Now ARC being the nice safe memory management system that it is tries to ensure that parameters are properly retained during the execution of a method. So even though my someMethod: was empty ARC was producing code that looked like this:

- (void)someMethod:(id)sender 
{
    objc_retain(sender);
    objc_release(sender);
}

但是,与此有关的问题是我正在调用performSelector:而不提供sender参数的值.因此,sender指向堆栈上的随机垃圾.因此,当调用objc_retain()时,应用程序崩溃了.

The problem with this however was that I was invoking performSelector: and not supplying a value for the sender parameter. So sender was pointing at random junk on the stack. Therefore when objc_retain() was invoked the app crashed.

如果我更改:

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的第二种形式的好处是,如果我调用不带参数的方法,它将仍然可以正常工作.

if I want to follow the 'standard' form of target-action methods that take a single parameter. The benefit of the second form of performSelector is that if I'm invoking a method that doesn't take a parameter it will still work fine.

这篇关于使用performSelector执行的方法中objc_retain崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆