Recently, I started to take over some iOS projects and all of them are OC code. Although I learned C language in college, I forgot all about it. @Weakify and @Strongify in the project made me wonder: what are they really for?

The standard way to break circular references

Suppose we have a Controller with a model property that updates the label when the data in the model changes. To this end, we built a model:

- (void)setUpModel { Model *model = [Model new]; // This block calls model.datachanged = ^(NSString *title) {self.label.text = title; }; self.model = model; }Copy the code

A few simple lines of code create a reference loop. Our controller refers to model, which in turn refers to a controller block. Fortunately, we can easily break the reference loop by introducing local variables with __weak and __strong store type modifiers:

Model *model = [Model new];

__weak typeof(self) weakSelf = self;
model.dataChanged = ^(NSString *title) {
  __strong typeof(self) strongSelf = weakSelf;
  strongSelf.label.text = title;
};

self.model = model;
Copy the code

You can see this kind of weak/strong freak code all over Objective-C projects. It’s the right way to do it, but it’s easy to get it wrong. As you introduce new features and make your blocks bigger and bigger, eventually someone will use self in them. We don’t notice when it happens, and the compiler only helps in the simplest cases. That’s where Weakify and Strongify macros come in.

@ weakify and @ strongify

The original implementations of @Weakify and @Strongify macros were more complex because they accepted multiple parameters. To make the analysis easier, I will specify my own version, each version accepts only one parameter:

#define weakify(var) __weak typeof(var) AHKWeak_##var = var;

#define strongify(var) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored "-Wshadow"") \
__strong typeof(var) var = AHKWeak_##var; \
_Pragma("clang diagnostic pop")
Copy the code

With these macros, our example takes the form:

Model *model = [Model new];

weakify(self);
model.dataChanged = ^(NSString *title) {
  strongify(self);
  self.label.text = title;
};

self.model = model;
Copy the code

It will compile to:

Model *model = [Model new];

__weak typeof(self) AHKWeak_self = self;
model.dataChanged = ^(NSString *title) {
  __strong typeof(self) self = AHKWeak_self;
  self.label.text = title;
};

self.model = model;
Copy the code

In the block self is masked by a local variable of the same name. Self can be used safely within a block because it refers to the local variable, which is strongly held but only valid until the block ends execution.

What happens if I forget to use Strongify? Weakify creates a new local variable, and if we don’t use it, we get a warning:

Unused variable ‘AHKWeak_self’

Note: This warning does not help if strongify(self) is used in multiple blocks. We can solve this by introducing a new scope for each block, for example:

{
  weakify(self);
  model.dataChanged = ^(NSString *title) {
    strongify(self);
    self.label.text = title;
  };
}

{
  weakify(self);
  model.syncFinished = ^(BOOL success) {
    strongify(self);
    [self update];
  };
}
Copy the code

It doesn’t look pretty, but it can be useful in some situations. If you forget to use Weakify but have Strongify, the compiler will display an error:

Use the undeclared identifier “AHKWeak_self”

The last

In some cases, when creating a new block, we still need to consider the possibility of preserving a reference to self (or some other object), which no macro can help with and requires practical analysis. However, when using Weakify and Strongify macros, any subsequent changes to the block are more secure than the standard weak/ Strong approach.