简体   繁体   English

NSInvocation 返回值但使用 EXC_BAD_ACCESS 使应用程序崩溃

[英]NSInvocation returns value but makes app crash with EXC_BAD_ACCESS

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.如果标志值为 nil,我正在调用一个生成调用对象并返回调用结果的方法。

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生成 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.在 for 循环中,没有对 NSInvocation ([self ....]) 的方法调用,循环就可以正常执行并且不会崩溃。 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.但是当我引入调用 NSInvocation 的方法时,我能够看到 for 循环中的 NSLog 打印预期的 NSArray 结果,但它崩溃并显示错误消息 EXC_BAD_ACCESS。

I am not able to figure out why it fails even though NSInvocation returns proper result.即使 NSInvocation 返回正确的结果,我也无法弄清楚它为什么会失败。 Without NSInvocation, for loop is not getting crashed.没有 NSInvocation,for 循环不会崩溃。

Any suggestions would be helpful.任何的意见都将会有帮助。

Thanks谢谢

I am guessing you are using ARC?我猜你正在使用ARC?

The problem is with the line [invocation getReturnValue:&resultSet];问题在于行[invocation getReturnValue:&resultSet]; . . getReturnValue: just copies the bytes of the return value into the given memory buffer, regardless of type. getReturnValue:只将返回值的字节复制到给定的内存缓冲区中,而不管类型如何。 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.由于resultSet是一个对象指针类型的__strong变量,ARC 假定已放入该变量的任何值都已保留,因此当它超出范围时将释放它。 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.) (此外,您的resultSet最初指向的数组将被泄漏,因为getReturnValue:覆盖该值而不释放它。为什么您甚至首先将该变量指向一个对象是我无法理解的。)

The solution is that you must give a pointer to a non-retained type to getReturnValue: .解决方案是您必须将指向非保留类型的指针提供给getReturnValue: Either:任何一个:

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

or:或者:

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

Yes, that's just happenedIn the ARC。是的,那只是发生在 ARC 中。

I guess this is the system Bug.估计是系统Bug。

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

my test demo download link https://github.com/leopardpan/IssuesDemo我的测试演示下载链接https://github.com/leopardpan/IssuesDemo

The original code原始代码

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

To solve the following解决以下问题

case 1:情况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:案例3:

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

case 4:案例4:

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

Recommended to use case1, principle should be @newacct said推荐使用case1,原理应该是@newacct说的
Welcome to discuss欢迎讨论

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM