NSInvocation returns value but makes app crash with EXC_BAD_ACCESS
Asked Answered
Y

3

26

I have an array which I am iterating and looking for a particular flag. If the flag value is nil, I am calling a method which generates an invocation object and returns the result of invocation.

My code structure is as follows

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);
        }
 }

Method which generates 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;
}

In the for loop, without the method call for NSInvocation ([self ....]), the loop just executes fine and not crashing. But when I introduce the method to invoke NSInvocation, I am able to see the NSLog in for loop prints expected NSArray result but it crashes with error message EXC_BAD_ACCESS.

I am not able to figure out why it fails even though NSInvocation returns proper result. Without NSInvocation, for loop is not getting crashed.

Any suggestions would be helpful.

Thanks

Yoshieyoshiko answered 25/2, 2014 at 15:10 Comment(7)
do you have more info on the crash reason from console or debugger?Redroot
Console doesn't show another information. All I get is Thread 1: EXC_BAD_ACCESS(code=EXC_i386_GPFLT)Yoshieyoshiko
Setup NSZombieEnabled. It'll give you more info. See #2190727Wehner
try to see if you can get information like described here: #13327050Redroot
Turned on zombies and this is what I get "2014-02-25 16:25:42.152 MyPlane[24339:70b] *** -[__NSArrayI release]: message sent to deallocated instance 0x1127717f0". Looks like a premature deallocation.Yoshieyoshiko
Declared NSArray *resultSet as a private variable rather than local variable and app is not crashing now.Yoshieyoshiko
Check out this answerSingleaction
I
89

I am guessing you are using ARC?

The problem is with the line [invocation getReturnValue:&resultSet];. getReturnValue: just copies the bytes of the return value into the given memory buffer, regardless of type. It doesn't know or care about memory management if the return type is a retainable object pointer type. Since resultSet is a __strong variable of object pointer type, ARC assumes that any value that has been put into the variable has been retained, and thus will release it when it goes out of scope. That is not true in this case, so it crashes. (Also, the array that you had resultSet originally point to will be leaked, since getReturnValue: overwrites that value without releasing it. Why you even made that variable point to an object in the first place is beyond me.)

The solution is that you must give a pointer to a non-retained type to getReturnValue:. Either:

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

or:

void *tempResultSet;
[invocation getReturnValue:&tempResultSet];
NSArray *resultSet = (__bridge NSArray *)tempResultSet;
Immensurable answered 26/2, 2014 at 7:0 Comment(3)
I had the same problem, and this solution worked like a charm! :)Counterstatement
Huge hugs and kisses for this.Kingdom
@Immensurable dude! I smelled something wrong with my invocation but, as it often happens, one rushes too much to read through the documentation and think... thanks a lot!Extrude
M
3

Yes, that's just happenedIn the ARC。

I guess this is the system Bug.

For example:
【iPhone4s + iOS8.4】、【 iphone 4 + iOS7.1】 (crash),
【iPhone6 + iOS9.3】、【 iphone 5 + iOS8.4.1】 (pass),

my test demo download link https://github.com/leopardpan/IssuesDemo

The original code

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

To solve the following

case 1:

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

case 2:

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

case 3:

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

case 4:

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

Recommended to use case1, principle should be @newacct said
Welcome to discuss

Myself answered 29/7, 2016 at 2:27 Comment(0)
C
0

This is the solution for cases where you don't know what type is the return value

__weak id weakReturnValue;
[_invocation getReturnValue:&weakReturnValue];
id returnValue = weakReturnValue; // ARC owned strong reference
Cima answered 1/3, 2020 at 13:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.