If a strong property has a block that refers to se

2019-05-31 04:35发布

For instance:

[self.contentWrapperView addGestureRecognizer:
   [UITapGestureRecognizer recognizerWithHandler:^(UIGestureRecognizer *sender, 
                                                   UIGestureRecognizerState state, 
                                                   CGPoint location) {
        if (self.customEditing) {
        [self setEditingMode:NO Animated:YES];
      }
    }]];

Where contentWrapperView is a strong property on self, and presuming contentWrapperView has a strong reference to the recognizer block. Is using self in the block going to lead to a retain cycle? This is the only part I don't quite understand.

2条回答
干净又极端
2楼-- · 2019-05-31 05:14

Think about a retain cycle in terms of what it actually is: a retention of yourself by an object that you own.

In Cocoa and Cocoa-Touch's terms, that means that any variable that you own strongly cannot, in turn, own you strongly. This is often gotten around by declaring a property that needs to own its parent weak, unsafe_unretained or assign. This gets complicated by blocks, which, acting as closures typically do, capture const or retained references to every variable within them (obviously, complicated even further by the __block qualifier).

Instead of looking at this from the standpoint of the block, I'll try to abstract it into an example with two classes, which is often more approachable for those in an OO lang:

Let's say I have class MyImportantObject, which needs to do some work with another class MyWorkerClass. We'd usually want a strong references to MyWorkerClass, because maybe we want it to stick around so we can do a little more work later on, or continue to call the same worker method over and over:

@interface MyImportantObject : NSObject 

@property (nonatomic, strong) MyWorkerClass *workerObj;

@end

In turn, our worker needs a stable reference to all of MyImportantObject's properties it needs to work (else it can't work!). So what blocks that reference self do, instead of taking a const or retained references to every variable, they take a retained reference to their parent!

@interface MyWorkerClass : NSObject 

@property (nonatomic, strong) MyImportantObject *workerObj;

@end

What this means is that if you attempted to use MyImportantObject's worker object, they'd retain each other, and neither would be deallocated properly! Huge no-no.

By shunting self into a __weak (or __block under MRC) pointer, we instead get a reference that looks like this:

@interface MyWorkerClass : NSObject 

@property (nonatomic, weak) MyImportantObject *workerObj;

@end

No more retain cycles, everybody's happy, everybody gets deallocated properly when the parent goes the way of the dinosaur.

But there is one more piece of the puzzle: What do we do about the weak reference? After all, let's say the good ol' compiler comes around and yanks self out from under you with a quick -release. Because of that zeroing weak pointer, you now have a nil pointer running amok in your block. This can lead to it literally doing nothing, while you sit there and scratch your head wondering why. This has led to the invention of, what I will term, the weak-strong dance.

J_mcnally's example is missing one crucial step in the dance, the "re-strongification" of your self pointer. But, why would we want to do that after we just went to all the trouble to avoid a retain cycle? It's because our strong copy of X isn't retained by the block, it's retained by the block's scope. In english, it means that our strong self will only deallocate once the end of the block has reached, thus guaranteeing not only a safe memory-cycle, but that it will not get deallocated out from under us. A proper fix looks like this:

__weak id weakSelf = self;
[self.contentWrapperView addGestureRecognizer:[UITapGestureRecognizer recognizerWithHandler:^(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location) {
    id strongSelf = weakSelf;
    if ([strongSelf customEditing]) {
        [strongSelf setEditingMode:NO Animated:YES];
    }
    //Do some other stuff before strongSelf passes out of scope.
}]];
查看更多
家丑人穷心不美
3楼-- · 2019-05-31 05:29

Yes it will lead to a retain cycle.

The workaround incase you care is

__weak id weakSelf = self;
[self.contentWrapperView addGestureRecognizer:[UITapGestureRecognizer recognizerWithHandler:^(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location) {
    if ([weakSelf customEditing]) {
        [weakSelf setEditingMode:NO Animated:YES];
    }
}]];
查看更多
登录 后发表回答