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.