In addition to using the __weak modifier to avoid circular references, there is one thing that is often easy to forget when using blocks. Apple calls it a “strong-weak Dance.”

Source of problem

This is a strong -> weak -> strong – reference transformation process. Before we figure out why we’re going to all this trouble, let’s take a look at what’s wrong with writing it in general.

__weak MyViewController *wself = self;
self.completionHandler = ^(NSInteger result) {
[wself.property removeObserver: wself forKeyPath:@"pathName"];
};
Copy the code

This avoids circular references, but we need to consider the following:

Assuming thatblockIs executed in a child thread and is in the process of executionselfThe main thread is released. Due to thewselfIs a weak reference and therefore automatically becomesnil. In KVO, this causes a crash.

Strong-Weak Dance

The solution is as simple as adding a line of code:

__weak MyViewController *wself = self;
self.completionHandler = ^(NSInteger result) {
__strong __typeof(wself) sself = wself; // strong reference once
[sself.property removeObserver: sself forKeyPath:@"pathName"];
};
Copy the code

This changes the reference count of the object to which self points to to 2. Even if self in the main thread is released due to overshooting on, the reference count of the object remains 1, avoiding object destruction.

thinking

During the discussion with his friends, he raised several questions. Although they are not difficult, they help to integrate all kinds of knowledge.

  1. Q: Does the following line of code assign a weakly referenced pointer to a strongly referenced pointer have the effect of a strong reference?
__strong __typeof(wself) sself = wself;
Copy the code

A: Yes. Reference counts describe objects, not Pointers. This means:

Sself forces a reference to the object that wself points to

So the object’s reference count is increased by one.

  1. Q:blockInternally definedsself, will not therefore strong referencesself?

A: No. A block refers to an external variable only if it intercepts it. If a new one is created internally, there is no problem.

  1. Q: If atblockThere is no strong reference internally, but throughifDo you agree or disagree with this statement?
__weak MyViewController *wself = self;
wself.completionHandler = ^(NSInteger result) {
if (wself) { The following code is executed only if wself is not nil
[wself.property removeObserver: wself forKeyPath:@"pathName"]; }};Copy the code

A: No! Given the multithreaded execution, maybe self hasn’t been freed at the time of the judgment, but when you execute the code inside self, it’s just been freed.

  1. Q: So in that sense,blockInternal strong-referencing doesn’t work either. maybeblockBefore execution,selfAnd he was released.

A: Yes! If self is freed before the block executes, the block’s reference count drops to zero, so it will be freed. Then it won’t be implemented at all. In addition, executing a nil closure will cause a crash.

  1. Q: If the command is executingblockIn the process of,blockWhat if they’re released?

A: Simply put, the block continues to execute, but the pointer it captures has an indeterminate value. See this article for more details

@ strongify and @ weakify

This is a macro defined in ReactiveCocoa. It can be used like this:

@weakify(self);
self.completionHandler = ^(NSInteger result) {
@strongify(self);
[self.property removeObserver: sself forKeyPath:@"pathName"];
};
Copy the code

This article does not analyze how they are implemented, so I will briefly explain two points:

  1. The “@” is useless here except to emphasize that the macro actually contains an empty AutoreleasePool, which is why the “@” is important.

  2. __weak MyViewController *wself = self; __weak MyViewController *wself = self; Code for this format:

#define rac_strongify_(INDEX, VAR) \\
__strong __typeof__(VAR) VAR = metamacro_concat(VAR, _weak_);
Copy the code

Case in Swift

Strong Weak Dance is also used in Swift thanks to @cyrus_dev. The easiest way to do this is to go straight to OC:

self.completionHandler = { [weak self] in
if let strongSelf = self {
/// ....}};Copy the code

The downside of this is that we can’t write if let self = self, so we need to redefine a variable, strongSelf, which is not very elegant.

You can also use the withExtendedLifetime function provided by the Swift library:

self.completionHandler = { [weak self] in
withExtendedLifetime(self) {
/ / /...}};Copy the code

The downside of this is that self is still optional and needs to be unsealed before it can be used.

Finally, another solution is to customize the withExtendedLifetime function:

extension Optional {
func withExtendedLifetime(body: T -> Void) {
if let strongSelf = self {
body(strongSelf)
}
}
}
Copy the code

Whether this is more elegant is debatable:

self.completionHandler = { [weak self] in
self.withExtendedLifetime {
/// self is represented by $0}};Copy the code

The resources

  1. What happens when a block is set to nil during its execution?
  2. The Weak/Strong Dance in Swift