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 для циклов печатает ожидаемый результат NSArray, но он выходит из строя с сообщением об ошибке EXC_BAD_ACCESS.

Я не могу понять, почему это не удается, хотя NSInvocation возвращает правильный результат. Без NSInvocation для цикла не сбой.

Любые предложения были бы полезными.

Спасибо

Ответы

Ответ 1

Я предполагаю, что вы используете 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;

Ответ 2

Да, это только что произошло в 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 указанным
Добро пожаловать, чтобы обсудить