Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Idiom to hide `self` in Objective C blocks?

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.

like image 943
Benjohn Avatar asked Mar 19 '14 16:03

Benjohn


People also ask

What is __ block Objective-C?

__block is a storage qualifier that can be used in two ways: Marks that a variable lives in a storage that is shared between the lexical scope of the original variable and any blocks declared within that scope. And clang will generate a struct to represent this variable, and use this struct by reference(not by value).

What is true about blocks in Objective-C?

Blocks are a language-level feature added to C, Objective-C and C++, which allow you to create distinct segments of code that can be passed around to methods or functions as if they were values. Blocks are Objective-C objects, which means they can be added to collections like NSArray or NSDictionary .

What is closure in Objective-C?

A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write @escaping before the parameter's type to indicate that the closure is allowed to escape.

How do you write blocks in Objective-C?

^blockName: Always remember that the block name is preceded by the ^ symbol. The block name can be any string you like, just like you name any other variable or method. Remember that both the ^ and the block name are enclosed in parentheses ().


1 Answers

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?

like image 73
Benjohn Avatar answered Oct 01 '22 04:10

Benjohn