Idiom to hide `self` in Objective C blocks?
Asked Answered
U

1

6

When I have an Objective C instance create a block that needs to refer to the instance, I frequently do so through a weak pointer that won't keep the instance alive and produce a retain cycle, like this:

__weak MyType *const weakSelf = self;
void (^aBlock)() = ^(){
  // Do things with weakSelf instead of self.
}

I'd like to have an idiom that prevents me from making use of the strong self in the block. Ideally, when using the idiom, I'd get a compile error if I try to use self in the block instead of weakSelf. A run time error would also be okay.

Urolith answered 19/3, 2014 at 16:1 Comment(5)
you can get a warning for that.. would that help?Shotwell
Latest versions of Xcode + ARC will detect strong reference cycles in blocks and give you a warning.Cynar
The warning is helpful, thanks. I've got a solution that I'll post. It's not perfect, but it might provoke a better solution.Urolith
Can the compiler warnings be turned in to errors?Urolith
Of course. You should always turn all compiler warnings to errors.Brose
U
1

I've got a solution for this that I don't especially like, but it might provoke a better answer. I'll leave this unanswered in the hope of a better solution arriving.

Here's one way to do it:

// Here's a method definition…
-(void) aMethod
{
  // Want to create a block in which its impossible to refer to strong "self".
  // Begin a new scope to do this in.
  {
    // Within this scope, cover the existing "self" with a weak variant.
    __weak STWeatherTableViewController const *weakSelf = self;
    __weak STWeatherTableViewController const *self = weakSelf;

    // Sadly it's _not_ possible to simply do:
    //   __weak STWeatherTableViewController const *self = self;
    // … it gives a warning about initialisation of a variable form its own
    // uninitialised value, which makes sense, though you might hope the
    // compiler would work out what's going on.

    // Make a block that captures the covered self and does something with it.
    void (^exampleBlock)() = ^(){ [self lineHeight]; };
    exampleBlock();
  }

  // Now, back in the scope of the original method, "self" is non weak
  // again.
  [self doSomething];
}

I guess, if you really cared a lot about this, you could use a macro. It would at least abstract the idea and make uses easy to find and notice in code:

#define WEAKEN_SELF(classType) \
__weak classType const *weakSelf = self; \
__weak classType const *self = weakSelf

Or even:

#define WEAKEN_SELF(classType) \
__weak classType const *weakSelfTemporary##__LINE__ = self; __weak classType const *self = weakSelfTemporary##__LINE__;

Which you'd use it like this:

-(void) testMethod
{
  // You still need that scope or you cover the original "self".
  {
    WEAKEN_SELF(STWeatherTableViewController)
    void (^exampleBlock)() = ^(){ [self someMethodOrOther]; };
    exampleBlock();
  }
}

I'm unconvinced it is worth the effort though. Having the compiler warnings is probably good enough and they can presumably be turned in to errors?

Urolith answered 21/3, 2014 at 11:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.