Sending 'NSError *const __strong *' to parameter of type 'NSError *__autoreleasing *' changes retain/release properties of pointer
Asked Answered
B

4

5

This question is similar to ios NSError types but the solution described there didn't work and I believe it isn't quite what I need.

I have a method that takes performs an asynchronous call and then invokes a completion block. When I try to pass the NSError ** to the completion block, I get this error:

Sending 'NSError *const __strong *' to parameter of type 'NSError *__autoreleasing *' changes retain/release properties of pointer

The code is as follows:

+(void) agentWithGUID:(NSString *) guid completion:(void (^)(AKAgentProfile * agentProfile, NSError ** error)) completionBlock
{
    dispatch_queue_t requestQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(requestQueue, ^{
        NSString * parameterizedUrl = [AKAgentProfileEndPoint stringByAppendingString:guid];
        NSURL *url = [NSURL URLWithString:parameterizedUrl];
        NSData *data = [NSData dataWithContentsOfURL:url];

        NSError * error = nil;

        AKAgentProfile * agentProfile = [[[AKAgentFactory alloc] init] agentProfileWithData:data error:&error];

        dispatch_async(dispatch_get_main_queue(), ^{
            completionBlock(agentProfile,&error);
        });

    });
}
Bethune answered 2/1, 2015 at 14:55 Comment(2)
You have some fundamental misunderstanding of what is pointer-to-pointer vs. pointer means.Daltondaltonism
Andy: I admit, it takes me a while to figure it out something that later turns out to be quite obvious!Bethune
D
6

Your completion block arguments are total nonsense.

You have a variable NSError* err on the call stack.

You then try to pass the address of err to a completion block that will be called in the main thread. By the time that completion block is called, your function has long returned, and &err is rubbish. If the completion block tried to store anything there, it would store an NSError* where some long time ago your err variable was on the stack, most likely overwriting some valuable data of a completely unrelated method.

This just doesn't work with callback blocks.

Detoxify answered 2/1, 2015 at 15:17 Comment(1)
is anybody comfortable writing ObjC Block syntax? No way. fuckingblocksyntax.comListing
K
5

Pass error by value, not by reference, i.e. change block signature to void (^)(AKAgentProfile * agentProfile, NSError * error) and pass error instead of &error.

Karankaras answered 2/1, 2015 at 15:0 Comment(0)
C
0

I had this in a different circumstance. Here's my code:

            NSError *_Nullable copyError = nil;
            ImpVirtualFileHandle *_Nullable writeFH = nil;

            [file getDataForkHFSPlusExtentRecord:extents];
            writeFH = [hfsPlusVol fileHandleForWritingToExtents:extents];
            bool const copiedDataFork = [file readDataFork:^bool(NSData *_Nonnull const data) {
                return [writeFH writeData:data error:&copyError] == data.length;
            } error:&copyError];

The error was:

Sending 'NSError * _Nullable const __strong *' to parameter of type 'NSError * _Nullable __autoreleasing *' changes retain/release properties of pointer

Given that we pass pointers to strong variables to arguments that take an autoreleasing pointer all the time, this error message doesn't do a lot to articulate why this time it's suddenly invalid.

Anyway, the actual problem is something the compiler completely forgot to mention: The variable is not declared with __block, and it needs to be for the block to be able to legitimately assign to it. (If it was a straight assignment, the compiler would've given a much more straightforward error saying so.)

With the addition of one simple keyword:

            __block NSError *_Nullable copyError = nil;

Now the block is authorized to change the contents of this variable, so the pass-by-reference is now legitimate and the error goes away.

Charger answered 12/3 at 4:59 Comment(0)
P
-2

You have error defined as argument in

+(void) agentWithGUID:(NSString *) guid completion:(void (^)(AKAgentProfile * agentProfile, NSError ** error)) completionBlock 

and then again in block, I suggest you rename the one in block like:

+(void) agentWithGUID:(NSString *) guid completion:(void (^)(AKAgentProfile * agentProfile, NSError ** error)) completionBlock
{
    dispatch_queue_t requestQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(requestQueue, ^{
        NSString * parameterizedUrl = [AKAgentProfileEndPoint stringByAppendingString:guid];
        NSURL *url = [NSURL URLWithString:parameterizedUrl];
        NSData *data = [NSData dataWithContentsOfURL:url];

        NSError * err = nil;

        AKAgentProfile * agentProfile = [[[AKAgentFactory alloc] init] agentProfileWithData:data error:&error];

        dispatch_async(dispatch_get_main_queue(), ^{
            completionBlock(agentProfile,&err);
        });

    });
}
Professor answered 2/1, 2015 at 15:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.