NSError initialization macro stopped working with recent Clang - objective-c

I am used to using this macro to initialize NSError** arguments:
#define INIT_ERROR_IF_NULL(error) error = error ? error : &(NSError*){ nil }
This checks the passed NSError** argument and if it’s NULL, initializes it to a dummy NSError so that I don’t have to wrap every *error dereference in a conditional. In code, this version:
- (void) doFoo: (NSError**) error
{
if (error) {
*error = [NSError …];
}
}
…turns into this:
- (void) doFoo: (NSError**) error
{
INIT_ERROR_IF_NULL(error);
*error = [NSError …];
}
I like the approach, it saves branching and makes me write less code (of course I don’t bother when the error is being used just once). The problem is that it stopped working in the recent Clang version, as the compiler complains about pointer type mismatch:
Pointer type mismatch ('NSError *__autoreleasing *' and 'NSError **')
If I change the macro to explicitly cast the resulting anonymous object to NSError *__autoreleasing* (the type signatures are getting ridiculous), the compiler complains further, on a different line:
Casting 'NSError **' to type 'NSError *__autoreleasing *' changes retain/release properties of pointer
Again, the variable in question is a method argument. I don’t understand the issue. What can I do to keep my macro?

This compiles without errors/warnings:
#define INIT_ERROR_IF_NULL(error) error = error ? error : &( NSError __autoreleasing *){ nil }

Related

__autoreleasing in error:(NSError *__autoreleasing *)outError

I noticed this pattern in Apple functions which return errors
error:(NSError *__autoreleasing *)outError
I understand the meaning, that it's pointer to pointer, used to carry out the result (using just * would change only the local copied variable, but not the outside one) but I'm concerned about the:
__autoreleasing
What happens if I leave it out? Do I get a leak? Why is it necessary?
You don't have to explicitly specify __autoreleasing when defining a function that
returns an object, for example
-(BOOL)doSomething:(NSError **)error;
The ARC compiler automatically inserts the __autoreleasing. This is explained in
the Clang/ARC documentation:
4.4.2 Indirect parameters
If a function or method parameter has type T*, where T is an
ownership-unqualified retainable object pointer type, then:
if T is const-qualified or Class, then it is implicitly qualified with
__unsafe_unretained;
otherwise, it is implicitly qualified with __autoreleasing.
The Xcode code completion
also knows about that and displays (NSError *__autoreleasing *)error.
When calling such a function the ARC compiler also automatically does
"the right thing", so you can just call
NSError *error;
BOOL success = [self doSomething:&error];
As explained in the "Transitioning to ARC Release Notes", the compiler inserts a temporary
__autoreleasing variable:
NSError *error;
NSError * __autoreleasing tmp = error;
BOOL success = [self doSomething:&tmp];
error = tmp;
(For the gory details you can read 4.3.4 "Passing to an out parameter by writeback" in
the Clang/ARC documentation.)

What am I doing wrong with this method definition and call?

I have this in my class header:
typedef void(^DBSuccessBlock)();
typedef void(^DBErrorBlock)(int errorNumber, NSString* description);
- (void) connect:(NSString*) path isFile:(BOOL) flag
success:(DBSuccessBlock) success
error:(DBErrorBlock) error;
This is how I'm trying to call the method:
[db connect:filePathName isFile:YES success:^{
// initialize db here if necessary
} error:^(int errorNumber, NSString *description) { //error on this line
NSLog(description);
return nil;
}];
The error line is giving me this compile error: Incompatible block pointer types sending 'void *(^)(int, NSString *_strong)' to parameter of type 'DBErrorBlock' (aka 'void (^)(int, NSString *_strong)')
The only difference I see is void* vs void and I'm not sure why. Can anyone help me figure out why I'm getting this error? Thanks.
You are returning the value nil in a block with return type void. Remove the return nil line.

Why can't the address of an ivar be passed to an "id __autoreleasing *" argument under ARC?

Under ARC, an out-parameter takes the following form (by default; this is equivalent to NSError **):
- (BOOL)tryWithError:(NSError *__autoreleasing *)err;
From the Transitioning to ARC Release Notes, if we pass the address of a __strong local variable, the compiler will create a temporary variable and generate the following code:
NSError *error; // strong
BOOL ok = [myObject tryWithError:&error];
// translated to
NSError *__strong error;
NSError *__autoreleasing tmp = error;
BOOL ok = [myObject tryWithError:&tmp];
error = tmp;
But if we do it with an instance variable:
#implementation Foo {
NSError *_error; // strong
}
- (void)bar
{
[myObject tryWithError:&_error];
}
...
this gives us the error
Passing address of non-local object to __autoreleasing parameter for write-back.
Why is this invalid? Couldn't the compiler just translate such code automatically to this?
- (void)bar
{
NSError *__autoreleasing tmp = _error;
[myObject tryWithError:&tmp];
_error = tmp;
}
After all, this is what I will be writing anyway to solve the problem!
Note: adding the out keyword to the parameter type will reduce the compiler's work slightly because it doesn't have to read the current value into the temporary variable — but this doesn't take care of the error.
A pointer to an ivar can't be passed to an “id __autoreleasing *” argument under ARC because that kind of pass-by-writeback is ill-formed. The respective section in the ARC specification lists legal forms of pass-by-writeback, the only one applicable here is
&var, where var is a scalar variable of automatic storage duration
with retainable object
, so only automatic storage duration (a local variable) is allowed.
Why this is invalid: I am pretty sure the reason here is compatibility with older code:
1) You should only look at the error writeback in the failure case. In the success case, there is no guarantee at all what's inside the error pointer.
2) In general, whether the writeback value should be used or not depends on the contract of the method. That is something the compiler cannot check.
This is the version of the code that matches the type of &error (NSError * __autoreleasing *) to the type of the writeback (NSError ** which is interpreted as NSError * __autoreleasing *). If ok is YES, the error value won't be touched.
NSError * __autoreleasing error;
BOOL OK = [myObject performOperationWithError:&error];
if (!OK) {
// use error
}
However, those __autoreleasing are ugly, so instead of forcing us to use __autoreleasing all over the place, the compiler allows us to pass a __strong (but local) variable as well (default ownership):
NSError *error;
BOOL OK = [myObject performOperationWithError:&error];
if (!OK) {
// use error
}
According to the docs, that gets rewritten to:
NSError * __strong error;
NSError * __autoreleasing tmp = error;
BOOL OK = [myObject performOperationWithError:&tmp];
error = tmp;
if (!OK) {
// use error
}
Not a problem at all, the error will only be used in the success case.
Now let's have a look at a __strong instance variable _error. Why doesn't the compiler allow that? Here is what the rewrite would look like:
NSError * __autoreleasing tmp = _error;
BOOL OK = [myObject performOperationWithError:&tmp];
_error = tmp;
if (!OK) {
// use error
}
The problem here is that the writeback in tmp would always be used (assigned to the instance variable _error), ignoring the contract of the method that the writeback should only be used in error cases (or in general whatever the documentation of the method says). A safe way to assign the last error to an instance variable would be
NSError * __autoreleasing tmp = _error;
BOOL OK = [myObject performOperationWithError:&tmp];
if (!OK) {
_error = tmp;
// use error
} else {
_error = nil; // Make sure that _error is nil if there was no error.
}
And that's only true for the convention of Cocoa's methods which return an error. In general there is no way for the compiler to tell what a method will do with an id *: There may be old methods out there that use different conventions. So if you really want to store the writeback in a __strong instance variable, you currently have to walk the extra mile yourself, and I don't expect this to change.

Understanding 'potential null dereference' when dealing with NSError

I understand that this may seems a very basic question, but I always thought that explaining (and understanding) basic question is the key to build clean and working code.
I have the following fragment of code in a CoreData entity:
-(BOOL)validateForInsert:(NSError *__autoreleasing *)error {
[super validateForInsert:error];
return [self validateInternal:error];
}
-(BOOL)validateInternal:(NSError *__autoreleasing *)error {
// checking attributes and build userInfo error
if(anyErrors) {
*error = [NSError errorWithDomain:appDomain code:200 userInfo:details];
return NO;
}
return YES;
}
by running 'analyze' in Xcode I get the famous 'potential null dereference' when creating error. While the compiler is not giving any alert, and the code has always worked.
This could be a question too, why the compiler doesn't warn me about that ?
But coming to the code, I perfectly know the solution, which is to check if (error != nil), but I am quite lost at this.
The question start at the beginning, at the meaning of NSError *_autoreleasing* , why the surrounding star ? Which should be a pointer to a pointer ?
Supposing I would like to call the validateForInsert: by myself, how can I build an *_autoreleasing* object ?
The following question, I suppose is related to the question above: if I am building the *error from scratch, why am I suppose to check if nil first ?
Last but not least, the code works fine, the errors get intercepted, could you please spot a case that can have it fail or crash ? As I said I am using CoreData, a generic example would be fine, but another related to CoreData is appreciated.
thanks
*error = [NSError errorWithDomain:appDomain code:200 userInfo:details];
is syntactically correct, therefore the compiler does not warn. The Analyzer detects that this will crash at runtime if error = NULL, i.e. if you call
[myObj validateForInsert:NULL];
If you call
NSError *error;
[myObj validateForInsert:&error];
then you pass the address of error, therefore you pass a variable of type NSError * *.
The __autoreleasing modifier is a hint to the ARC compiler that the object assigned to error is an autoreleased object.
You can declare a variable to be autoreleasing with
NSError * __autoreleasing error;
[myObj validateForInsert:&error];
but normally you do not care. ARC just generates the correct code.
For details, read the Transitioning to ARC Release Notes. There are also lots of answers on this topic on stackoverflow.

NSError * vs NSError **

I realize that this is similar to an existing post here, What's the Point of (NSError**)error?, but my question is a little different. I understand how the double pointer works, and how this is the common iOS API error pattern. My question is more around the single pointer, and why this code doesn't work:
- (BOOL)someMethodWithError:(NSError *)error
{
...
if( errorOccured )
{
NSError *e = [[[NSError alloc] initWithDomain:#"" code:1 userInfo:nil] autorelease];
error = e;
return NO;
}
return YES;
}
implemented using:
NSError *error = nil;
if( ![someObj someMethodWithError:error] )
{
NSLog(#"Error: %#", [error localizedDescription]);
}
Why doesn't the assignment in the method implementation reassign the pointer to the new NSError object?
I find that it helps to think of a pointer as just an integer. (That's what it is.)
Look at your example with an int.
-(BOOL)someMethodWithError:(int)error
{
error =100;
return NO;
}
That integer is pass by value. after that function is called error will not change.
error = 123;
[self someMethodWithError:error];
//error is = 123;
The pointer is the same thing. It's pass by value.
NSError * error; //this assigns this to a value in memory.
NSLog(#"%p",error);
[self someMethodWithError:error];
NSLog(#"%p",error); // the value of error will not have changed.
if you want the pointer to change you need to send in a pointer to that pointer and change it. It is confusing but draw yourself a diagram of memory and think about it. :)
It's all about the double pointer.
Why isn't the new value showing? Because you never modified the contents of the object which you initialized as nil. To do that you would have needed to use a reference to the memory location where the nil value was stored (a pointer to the pointer), not just a copy of the nil value (which is what you are passing using the single pointer).
This is a really common thing to do in C where some of the output of a function is "returned" through a pointer in the input arguments.
This is how the C language works. You might like to take a look at this question and answer:
C double pointer, which is basically the same thing in a different guise.
Well error that somemethodWithError has is different pointer which is containing the address of the error from where someMethodWithError is get called. so when you assign any object to error pointer of someMethodWithError it will not reflect in the error pointer of method from where it is called
it's like
NSError * error1 = obj1;
NSError * error2 = error1;
and afterward if you assign any new object to error2 like
error2 = obj2;
it will not change the pointer value of error1 and it will still point to obj1