2012-08-09 39 views
10

ARCが自分のコードにobjc_retainsを自動挿入することに関連して、この異常なクラッシュが発生しました。performSelectorで実行されたメソッドでobjc_retainがクラッシュする

私は、次の2つのクラスがあります。私のようなのMenuItemのインスタンスをいくつかの時点で

@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 *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 

どうしてですか?

答えて

17

クラッシュの原因は、間違った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 withObject:nil]; 

[self.target performSelector:self.action]; 

を変更することができます。performSelectorの2番目の形式の利点は、パラメータを受け取っていないメソッドを呼び出すと、正常に動作するということです。

関連する問題