Re: ARC issue
Re: ARC issue
- Subject: Re: ARC issue
- From: Ken Thomases <email@hidden>
- Date: Wed, 07 Nov 2012 07:50:23 -0600
On Nov 7, 2012, at 7:18 AM, Marco Tabini wrote:
> On 2012-11-07, at 8:05 AM, Andreas Grosam <email@hidden> 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@hidden)
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:
This email sent to email@hidden
References: | |
| >ARC issue (From: Andreas Grosam <email@hidden>) |
| >Re: ARC issue (From: Marco Tabini <email@hidden>) |