I'm trying to use the pointer of an object declared outside an Objective-C block, inside the block itself. For example:
NSError* error = nil;
[self invokeAsync:^id{
return [self doSomething:&error];
}];
I get a compiler error on the third line telling me:
Sending 'NSError *const__strong *' to parameter of type 'NSError *__autoreleasing *' changes retain/release properties of pointer
Why is that?
The compiler message is confusing, but is telling you that you have a type mismatch.
But, doesn't matter, because that code makes no sense.
An asynchronous invocation cannot set state in the calling thread's stack. I.e. there is no way that error
can be set to a meaningful value.
That is, the method invokeAsync:
will return before the work block is executed. Thus, there is no way to return anything meaningful from invokeAsAsync:
to indicate the success/failure of the execution of the block.
If you want to invoke something asynchronously with an error, you'll need a callback:
[self invokeAsync:^id{
NSError *e;
if ([self doSomething:&e])
[self errorHappened:e];
else
[self asyncThingyDone];
}];
There are two issues here. The first is the timing issue which is already pointed out by @bbum.
The other may be what you are asking for, i.e., why the compiler gives such an error. Also as @bbum said, "The compiler message is confusing". In order to decouple the second issue from the first one, let's assume your call is invokeSyncAndWait:
instead of invokeAsync:
. Now the timing issue is gone we can focus on the second issue:
NSError* error = nil;
[self invokeSyncAndWait:^id{
return [self doSomething:&error];
}];
The error
variable captured in the block is just a by-value copy, not a real reference:
Stack (non-static) variables local to the enclosing lexical scope are captured as const variables. Their values are taken at the point of the block expression within the program. In nested blocks, the value is captured from the nearest enclosing scope.
Because you don't have a real reference to the variable error
in the block, you can not take the address of it. That's why the compiler refuses to compile your code.
To get a by-reference variable, you should use __block
:
Variables local to the enclosing lexical scope declared with the __block storage modifier are provided by reference and so are mutable. Any changes are reflected in the enclosing lexical scope, including any other blocks defined within the same enclosing lexical scope. These are discussed in more detail in “The __block Storage Type.”
So the working code is:
__block NSError* error = nil;
[self invokeSyncAndWait:^id{
return [self doSomething:&error];
}];
It is still perilous code though:
Thus, the address of a __block variable can change over time.
I just explained the compiling issue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With