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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11875900/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 01:11:17  来源:igfitidea点击:

Crash in objc_retain in method performed with performSelector

xcodeautomatic-ref-countingperformselector

提问by orj

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

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

I have the following two classes:

我有以下两个类:

@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

At some point I instantiate a MenuItem as such:

在某些时候,我将 MenuItem 实例化为:

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

Then elsewhere I invoke performActionon the menu item:

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

 [item performAction];

In the implementation of someMethodI get a crash:

在执行someMethod我得到一个崩溃:

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

Why is this happening?

为什么会这样?

回答by orj

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

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

NSObjectdefines multiple versions of performSelector. The one I was invoking was:

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

- (id)performSelector:(SEL)aSelector;

However the method I was invoking took an idparameter. Eg:

但是,我调用的方法采用了一个id参数。例如:

- (void)someMethod:(id)sender;

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:

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

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

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

然而,问题在于我正在调用performSelector:而不是为sender参数提供值。于是sender指着堆在随机的垃圾。因此,当objc_retain()被调用时,应用程序崩溃了。

If I change:

如果我改变:

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

to

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

and

- (void)someMethod:(id)sender;

to

- (void)someMethod;

Then the crash goes away.

然后崩溃消失了。

Similarly I can also change

同样我也可以改变

[self.target performSelector:self.action];

to

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

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

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