Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is self retained in a block that accesses one of its properties?

In the code below, self is retained to assure that the image object lives when the block gets called. That's what the docs say. However, I don't seem to understand why. Simply retaining the image would have guarantee that it doesn't get deallocated. So why retain self as well?

self.finishBlock = ^{
    self.image.hidden = YES;
}

Does this apply if you access the image directly?

self.finishBlock = ^{
    _image.hidden = YES;
}
like image 898
Meda Avatar asked Dec 26 '22 10:12

Meda


2 Answers

self is retained because

self.image.hidden = YES;

is actually

[[self image] setHidden:YES];

The image isn't/can't be retained directly because it's not available until the block is executed and [self image] is called to obtain the image.

Your second example also retains self, though for a slightly different reason. In Objective-C, when you access an instance variable directly, it's actually accessed via self's underlying struct. So, _image is actually self->_image after compilation. Again, the block needs access to self, so it retains self.

Also worth noting is that in either case, if the value of _image changes before the block is executed, the block will "see" the new value. That is often, but not always what you want.

You have two options to avoid retaining self. The first will do so and will capture the value of _image at the time the block is defined, so even if it changes, the block will see the original value. This approach is to define a local variable, set it to the current value returned by self.image, then use that in the block:

UIImage *image = self.image;
self.finishBlock = ^{
    image.hidden = YES;
}

The other approach is to capture a weak version of self and use that in the block. In this case, the block will have a weak -- instead of strong -- reference to self (ie. won't retain self). However, the -image accessor method on self will still be called, so if image is changed before the block runs, the new value will be used:

__weak YourClass *weakSelf = self;
self.finishBlock = ^{
    weakSelf.image.hidden = YES;
}

Note that in this case, if self is deallocated before the block runs, weakSelf will be nil, and the statement in the block will effectively be a NOOP (message sends to nil don't do anything in Objective-C).

like image 71
Andrew Madsen Avatar answered May 04 '23 00:05

Andrew Madsen


A block needs to retain any captured objects in the block. Your first block example is really:

self.finishBlock = ^{
    [[self image] setHidden:YES];
}

The block must retain self so it can properly call the image method. As written the block can't simply retain image because the image isn't obtained until the block is executed and the image method is called. So the only option here is to retain self.

In the second block you really have:

self.finishBlock = ^{
    self->_image.hidden = YES;
}

so again, self must be retained so the proper value of the _image ivar is accessed when the block is actually executed.

like image 38
rmaddy Avatar answered May 04 '23 00:05

rmaddy