orj*_*orj 10 xcode automatic-ref-counting performselector
我在我的代码中有一个与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
Run Code Online (Sandbox Code Playgroud)
在某些时候,我实例化一个MenuItem:
MenuItem *item = [MenuItem alloc] initWithTarget:widget action:@selector(someMethod:) object:nil];
Run Code Online (Sandbox Code Playgroud)
然后我performAction
在其他地方调用菜单项:
[item performAction];
Run Code Online (Sandbox Code Playgroud)
在执行中someMethod
我遇到了崩溃:
@implementation Widget
- (void)someMethod:(id)sender
{
// EXEC_BAD_ACCESS crash in objc_retain
}
@end
Run Code Online (Sandbox Code Playgroud)
为什么会这样?
orj*_*orj 17
崩溃的原因是因为我使用了错误performSelector
.
NSObject
定义了多个版本performSelector
.我调用的那个是:
- (id)performSelector:(SEL)aSelector;
Run Code Online (Sandbox Code Playgroud)
但是我调用的方法采用了一个id
参数.例如:
- (void)someMethod:(id)sender;
Run Code Online (Sandbox Code Playgroud)
现在ARC是一个很好的安全内存管理系统,它试图确保在执行方法期间正确保留参数.所以即使我someMethod:
是空的,ARC 也会生成如下所示的代码:
- (void)someMethod:(id)sender
{
objc_retain(sender);
objc_release(sender);
}
Run Code Online (Sandbox Code Playgroud)
然而,问题在于我正在调用performSelector:
而不是为sender
参数提供值.因此sender
指向堆栈上的随机垃圾.因此,当objc_retain()
被调用时,应用程序崩溃了.
如果我改变:
MenuItem *item = [[MenuItem alloc] initWithTarget:widget
action:@selector(someMethod:)
object:nil];
Run Code Online (Sandbox Code Playgroud)
至
MenuItem *item = [[MenuItem alloc] initWithTarget:widget
action:@selector(someMethod)
object:nil];
Run Code Online (Sandbox Code Playgroud)
和
- (void)someMethod:(id)sender;
Run Code Online (Sandbox Code Playgroud)
至
- (void)someMethod;
Run Code Online (Sandbox Code Playgroud)
然后崩溃就消失了.
同样我也可以改变
[self.target performSelector:self.action];
Run Code Online (Sandbox Code Playgroud)
至
[self.target performSelector:self.action withObject:nil];
Run Code Online (Sandbox Code Playgroud)
如果我想遵循采用单个参数的"标准"形式的目标 - 动作方法.第二种形式的好处performSelector
是,如果我正在调用一个不带参数的方法,它仍然可以正常工作.
归档时间: |
|
查看次数: |
9515 次 |
最近记录: |