On Nov 7, 2012, at 7:18 AM, Marco Tabini wrote:

> On 2012-11-07, at 8:05 AM, Andreas Grosam <agro...@onlinehome.de> 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 (Cocoa-dev@lists.apple.com)

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 arch...@mail-archive.com

Reply via email to