On Nov 7, 2012, at 7:18 AM, Marco Tabini wrote:
> On 2012-11-07, at 8:05 AM, Andreas Grosam <[email protected]> wrote:
>
>> NSDictionary* fetchUser(NSNumber* ID, NSError** error)
>> {
>> id user = nil;
>> //@autoreleasepool // crashes when @autoreleasepool is enabled
>> {
>> id data = ...; // response body of a HTTP Response (NSData) or NSError
>> object, never nil.
>> if ([data isKindOfClass:[NSData class]]) {
>> user = [NSJSONSerialization JSONObjectWithData:data
>> options:0
>> error:error];
>> }
>> else if (error) {
>> *error = data;
>> }
>> } // autoreleasepool
>> return user;
>> }
>
> I wonder if the problem might be that data is an autoreleased object, which
> automatically gets dealloc'ed at the end of the autorelease pool (as
> explained in the docs). Have you tried replacing
>
> *error = data
>
> with
>
> *error = [data copy]
>
> and seeing what happens?
I'm guessing that won't change anything. The problem, I think, is that for
parameters like "error" which are returned indirectly via parameters, the
compiler applies an implicit __autoreleasing qualifier. Assigning to an
__autoreleasing variable has the effect of discarding the old value (no
-release because it was already -autoreleased) and doing a -retain and
-autorelease on the new value. Since you've wrapped that in an autorelease
pool, as that pool is exited, the pointed-to error object is actually released,
leaving error pointing to an object that may have been deallocated.
At the call site, your "err" object has implicit __strong qualification. It's
documented that the compiler resolves the mismatch between the qualifiers by
(effectively) creating a temporary __autoreleasing variable, passing the
address of that, and then, on return, assigning that to the __strong variable:
NSError* err = nil;
__autoreleasing NSError* temp = err;
NSDictionary* deletedUser = this->fetchUser(userID, &temp);
err = temp;
EXPECT_TRUE(deletedUser == nil); <== the crash occurs **before**
this statement, but **after** the return statement of the function fetch user.
That assignment causes a -release to err's old value (which is a no-op since
err is nil) and a -retain to its new value. I think that -retain is being sent
to a deallocated object, which is the cause of your crash.
What happens if you enable zombies?
This seems like a problem with ARC. Ideally, the compiler would understand not
just that "error" is __autoreleasing but would understand something about its
"autorelease scope". That is, it needs to survive to the caller's scope and so
it needs to survive the @autoreleasepool block, so the compiler should retain
it in the block and autorelease it outside.
That said, I think the solution may be to declare a local, implicitly strong
NSError pointer at the same scope as "user", use that within the
@autoreleasepool block, and then assign from that to the output parameter, if
it's non-NULL, outside of the block.
Regards,
Ken
_______________________________________________
Cocoa-dev mailing list ([email protected])
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com
This email sent to [email protected]