views:

4799

answers:

2

Which is right? This:

NSArray* foo = [[NSArray alloc] initWithObjects:@"a", @"b", nil];
[bar performSelectorInBackground:@selector(baz:) withObject:foo];

- (void)baz:(NSArray*)foo {
    ...
    [foo release];
}

Or:

NSArray* foo = [[[NSArray alloc] initWithObjects:@"a", @"b", nil] autorelease];
[bar performSelectorInBackground:@selector(baz:) withObject:foo];

- (void)baz:(NSArray*)foo {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    ...
    [pool release];
}

I know the first one works, but Clang complains about it, so I wonder if there's a better pattern to use.

I would "just try out" the 2nd one, but with autoreleasing, who knows whether the absence of EXC_BAD_ACCESS means that you're doing it right or that you just got lucky...

+8  A: 

First is wrong.

performSelectorInBackground:withObject: retains both bar and foo until task is performed. Thus, you should autorelease foo when you create it and let performSelectorInBackground:withObject take care of the rest. See documentation

Latter is correct because you autorelease foo when you create it. Autorelease pool that you create inside baz has nothing do with correctness of foo's memory management. That autorelease pool is needed for autoreleased objects inside pool allocation and release in baz, it doesn't touch foo's retain count at all.

tequilatango
Oh, it's right there in the documentation! Silly me. :)
lawrence
I should clarify: you MUST create and drain an autorelease pool inside -baz:, unless you KNOW that nothing will be sent an -autorelease method inside there. The best rule of thumb is to assume that will happen and create/drain an autorelease pool, as in example 2. But use [pool drain], not [pool release].
Jim Dovey
Exactly as Jim Dovey said: you usually need to create autorelease pool just like you do for main function (see Thread Programming Guide). It's just important to understand that this pool has nothing to do with autorelease of foo.
tequilatango
@Jim, the docs stress that if you are in a memory-managed environment (which I believe we are, iPhone OS), calling `drain` is equivalent to calling `release`.
Jacob Relkin
True— but -drain will also do the appropriate thing for any environment, so use it, in case your code ever moves to the desktop or collected memory arrives on the iPhone.
Jim Dovey
A: 

IMHO this would be better:

NSArray* foo = [[NSArray alloc] initWithObjects:@"a", @"b", nil];
[bar performSelectorInBackground:@selector(baz:) withObject:foo];
[foo release];
Shaikh Sonny Aman
Not only does it have little or no benefit, it would be good if posting an alternative solution to explain why you think it's a better solution (or its merits).
buggles
This will not worse. [bar performSelectorInBackground:@selector(baz:) withObject:foo]; will create a thread and return immediately leaving the baz to run in a separate thread. if you release foo immediately after, that may cause problem for functions inside baz. for e.g., somewhere inside baz may need foo but see foo has been dealloced.
Jack
Jack, your comment is not correct. The code above is also perfectly valid, but it's no better or worse than the autorelease method. performSelectorInBackground:withObject: will retain foo before it returns so it's safe to release it straight away. Also, make no assumptions about whether baz: will be executed first or the performSelectorInBackground:withObject: message returns first - either could happen, i.e. baz: could have completed before [foo release] or [foo release] could happen before baz: has even started.
jhabbott