web-dev-qa-db-ja.com

NSInvocationは値を返しますが、EXC_BAD_ACCESSでアプリがクラッシュします

反復して特定のフラグを探している配列があります。フラグ値がnilの場合、呼び出しオブジェクトを生成し、呼び出しの結果を返すメソッドを呼び出しています。

私のコード構造は次のとおりです

for(NSString *key in [taxiPlanes allKeys])
{
        Plane *currentPlane = [taxiPlanes objectForKey:key];

        if(currentPlane.currentAction == nil)
        {
            NSString *selector = [[currentPlane planeTakeoffSequence] firstObject];
            currentPlane.currentAction = selector;

            // Calling for NSInvocation in [self ...]
            NSArray *action = [NSArray arrayWithArray:[self operationFromTakeoffAction:currentPlane.currentAction AtPoint:currentPlane.position]];

        NSLog(@"%@",action);
        }
 }

NSInvocationを生成するメソッド

-(NSArray *) operationFromTakeoffAction:(NSString *) action AtPoint:(CGPoint) flightPoint
{
    NSMethodSignature *methodSignature = [FlightOperations instanceMethodSignatureForSelector:NSSelectorFromString(action)];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];

    [invocation setTarget:fOps];
    [invocation setSelector:NSSelectorFromString(action)];
    [invocation setArgument:&flightPoint atIndex:2];

    NSArray *resultSet = [NSArray alloc]init];
    [invocation invoke];
    [invocation getReturnValue:&resultSet];

    return resultSet;
}

Forループでは、NSInvocation([self ....])のメソッド呼び出しがなくても、ループは正常に実行され、クラッシュしません。しかし、NSInvocationを呼び出すメソッドを導入すると、NSLog in forループが期待されるNSArrayの結果を出力するのを確認できますが、エラーメッセージEXC_BAD_ACCESSでクラッシュします。

NSInvocationが適切な結果を返しても、なぜ失敗するのか理解できません。 NSInvocationがないと、forループはクラッシュしません。

どんな提案も役に立ちます。

ありがとう

24
slysid

ARCを使用していると思いますか?

問題は行[invocation getReturnValue:&resultSet];にあります。 getReturnValue:は、タイプに関係なく、戻り値のバイトを指定されたメモリバッファにコピーするだけです。戻り値の型が保持可能なオブジェクトポインタ型であるかどうかは、メモリ管理を認識または考慮しません。 resultSetはオブジェクトポインタ型の__strong変数であるため、ARCは変数に入力された値が保持されていると見なし、スコープ外になると解放します。この場合はそうではないので、クラッシュします。 (また、resultSetが最初に指していた配列は、getReturnValue:が値を解放せずに上書きするため、リークされます。そもそもその変数がオブジェクトを指すようにした理由は、私。)

解決策は、保持されていない型へのポインターをgetReturnValue:に指定する必要があることです。どちらか:

NSArray * __unsafe_unretained tempResultSet;
[invocation getReturnValue:&tempResultSet];
NSArray *resultSet = tempResultSet;

または:

void *tempResultSet;
[invocation getReturnValue:&tempResultSet];
NSArray *resultSet = (__bridge NSArray *)tempResultSet;
78
newacct

はい、それはARCで起こったばかりです。

これはシステムのバグだと思います。

例えば:
【iPhone4s + iOS8.4】、【iphone 4 + iOS7.1】(クラッシュ)、
【iPhone6 + iOS9.3】、【iphone 5 + iOS8.4.1】(合格)、

私のテストデモのダウンロードリンク https://github.com/leopardpan/IssuesDemo

元のコード

NSArray *resultSet = [NSArray alloc]init];
[invocation invoke];
[invocation getReturnValue:&resultSet];

以下を解決するには

ケース1:

void *temp = NULL;   
[invocation invoke];
[invocation getReturnValue:&temp];   
NSArray *resultSet = (__bridge NSArray*)temp; 

ケース2:

__weak NSArray *resultSet = [NSArray alloc]init];
[invocation invoke];
[invocation getReturnValue:&resultSet];

ケース3:

__autoreleasing NSArray *resultSet = [NSArray alloc]init];
[invocation invoke];
[invocation getReturnValue:&resultSet];

ケース4:

__unsafe_unretained NSArray *resultSet = [NSArray alloc]init];
[invocation invoke];
[invocation getReturnValue:&resultSet];

Case1の使用をお勧めします。原則は@newacctである必要があります。
話し合いへようこそ

2
leopardpan

これは、戻り値のタイプがわからない場合のソリューションです。

__weak id weakReturnValue;
[_invocation getReturnValue:&weakReturnValue];
id returnValue = weakReturnValue; // ARC owned strong reference
0
chunkyguy