Resolving retain cycles: is there really a reason to add __strong *strongSelf when we already have __weak *weakSelf?

StackOverflow https://stackoverflow.com/questions/22996536

Pregunta

What if I have a class Foo declared like:

@interface Foo : NSObject
@property (copy, nonatomic) void (^aBlock)(void);
- (void)bar;
@end

@implementation Foo
- (void)bar {}
@end

and I store in Foo instance a block that captures this instance itself:

Foo *foo = [Foo new];

__weak Foo *weakFoo = foo;
foo.aBlock = ^{
    __strong Foo *strongFoo = weakFoo; <--- does this __strong Foo * really makes sense?

    [strongFoo bar];
}

... then somewhere later use foo.aBlock()

or I just do:

Foo *foo = [Foo new];

__weak Foo *weakFoo = foo;
foo.aBlock = ^{
    [weakFoo bar];
}

... then somewhere later use foo.aBlock()

Does adding of __strong *strongFoo = weakFoo makes any sense or is it enough to use just [weakFoo bar] inside a block?

¿Fue útil?

Solución

In your example it's not strictly necessary to make a strong reference inside the block again: weakFoo will either be nil and the method is not called, or it's a valid reference and will stay valid for the duration of this call.

But consider the following snippet:

Foo *foo = [Foo new];

__weak Foo *weakFoo = foo;
foo.aBlock = ^{
    [weakFoo bar];
    [weakFoo baz];
}

... then somewhere later use foo.aBlock()

Now you introduced a race condition, where weakFoo could be valid for the first call, but nil for the second call. This is almost never what you want. In this case use __strong Foo *foo = weakFoo; to make sure you have the same value for both calls.

Edit:

Here is an example how you can trigger such a race condition:

@interface BlockHolder : NSObject
@property (nonatomic, copy) void(^block)(void);
@end

@implementation BlockHolder
@end


int main(int argc, const char * argv[])
{
    @autoreleasepool {
        BlockHolder *holder = [[BlockHolder alloc] init];

        __weak BlockHolder *weakHolder = holder;
        holder.block = ^{
            for (int i = 0; i < 10; i++) {
                NSLog(@"weakHolder is %@", weakHolder);
                sleep(1);
            }
        };

        dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);

        dispatch_async(queue, holder.block);

        sleep(2); // give the queue some time to start executing the block
        holder = nil;

        dispatch_sync(queue, ^{}); // wait for the queue to finish
    }
    return 0;
}

Otros consejos

You should use:

__weak NSObject * weakFoo = foo;  // Assign foo to weakFoo to avoid retain cycles
foo.block =
^{
    NSObject * strongFoo = weakFoo; // By default it is strong, no need for __strong prefix. Hold the weakFoo to make sure it is not nil out while block is executing.
    if (strongFoo != nil) // Nil check if you want to execute code in block if non-nil.
    {
       [strongFoo useIt];
    }
}

There is a reason. The thing is, a __weak reference won't keep the object alive during the block. So, for example, if you have an asynchronous block, weakSelf could disappear halfway through the block's execution (because the weak reference doesn't keep the object alive). So we use a strongSelf inside the block to ensure that the object lives long enough to make it all the way through the block.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top