Collapse caused by weakself and strongself

Time:2022-1-25

preface

stayOCIn, we often encounter something calledCircular reference, no doubt,Circular referenceIt will lead to memory leakage. In severe cases, it is also possible to lead to application crash. We often encounterCircular referencenamelyBlock(ordelegate)Caused by, and the way to solve it is also the use of platitudesweakTo weakly reference the referenced object and break the loop, so as to avoid the problem of circular reference.

However, if you are a little careless, sometimes use itweakIt will also lead to the collapse of the application, resulting in irreparable consequences. This article is a brief description of how to use it correctlyweakAnd sometimes it needs to be combinedstrongTo avoidCircular referenceMemory leak.

Block circular reference

One object holds oneBlock, thisBlockThis object is referenced again in. This isCircular reference。 The most common and simplest is to hold the currentself, which is often encountered in development. staySecondViewController.mFor example:

#import "SecondViewController.h"
#import "MyObject.h"

@interface SecondViewController ()
@property (nonatomic, strong) MyObject *obj;
@property (nonatomic, copy) NSString *name;
@end

@implementation SecondViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.title = @"Second VC";
    self.name = @"Alice";
    
    self.obj = [[MyObject alloc] init]; 
    [self.obj start:^{
        NSLog(@"name: %@", self.name);
    }];
    
}

- (void)dealloc
{
    NSLog(@"OOPS! ⚠️⚠️⚠️ %s", __PRETTY_FUNCTION__);
}

@end

stayMyObject.hin

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

typedef void (^ExecuteBlock)(void);

@interface MyObject : NSObject

- (void)start:(ExecuteBlock)block;

@end

NS_ASSUME_NONNULL_END

MyObject.min

#import "MyObject.h"

@interface MyObject()
@property (nonatomic, copy) ExecuteBlock myBlock;
@end

@implementation MyObject

- (instancetype)init
{
    self = [super init];
    if (self) {
        [self initData];
    }
    return self;
}

- (void)initData
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        if (self.myBlock) {
            self.myBlock();
        }
    });
}

- (void)start:(ExecuteBlock)block
{
    self.myBlock = block;
}

@end

obviously,selfholdobjobjholdblockstartMethod parametersblock), blockHold againselfnameIs currentselfA property of), thus causing obvious problemsCircular reference

This kind of treatment is also very simple, aweakYou can do it:

self.obj = [[MyObject alloc] init];
__weak typeof(self) weakSelf = self;
[self.obj start:^{
    NSLog(@"name: %@", weakSelf.name);
}];

Is there any problem with our handling here? The answer isNo problem

Then we putnamechange intoMember variable, i.e.:

@interface SecondViewController ()
{
    NSString *name;
}
@property (nonatomic, strong) MyObject *obj;
@end

......
self.obj = [[MyObject alloc] init];
__weak typeof(self) weakSelf = self;
[self.obj start:^{
    __strong typeof(weakSelf) strongSelf = weakSelf;
    NSLog(@"name: %@", strongSelf->name);
}];
...... 

Note that due tonameyesMember variable, cannot be usedweakSelfTo quotenameBecause it’s aWeak pointer。 Therefore, we must be right hereweakSelfMake a strong reference, that is, usestrongSelfTo quotename

Think about it, when the user enters the page, in10sReturn to the previous page in, and waitblockWhat happens when it is executed?Application crash!!!

Crash analysis

Strange! Why does this problem happen? It feels like there’s no problem. Use itweakSelfAvoid circular references and usestrongSelfTo refer to member variables, how can it collapse? WhennameWhen it is an attribute, that isweakSelf.nameThere will be no problem, justnamefromattributebecomeMember variableNot anymore?

In fact, as long as you understandOCMediummessage sendingMechanism, you can basically know the reason for the collapse mentioned above. stayOCYes, yesnilSend message yes“Legal”Therefore, when usedPoint grammarWhen you come to visit, you actually visitattribute(here is)name)YesgetterMethod, so there will be no collapse.

However, if it is acquisitionMember variableIf so, it is not a method, but throughThe self pointer directly accesses the memory address of its internal member variableAt this point, when the page has been released,selfNo longer exists,strongSelfmeannil, it is conceivable that for a non-existent“Object”, visit the so-called“Member variable”, i.enil -> name, it’s no surprise that we’re going to collapse.

Crash resolution

I. try to use attributes

This approach is essentially based onOCMessage mechanism, rightnilSending messages is allowed. So evenselfIf it is destroyed, there will be no problem because you passstrongSelf.nameThe method is called,OCYour messaging mechanism allows you to do this. Although sometimes we use it directly for performance reasonsMember variableBecause there is a price for calling methods, but in most cases, the performance considerations brought by this are acceptable and can be ignored.

Therefore, to be absolutely safe, inblockInternal useattributeIt is a feasible and effective way to obtain.

Second, member variables are still usedweakSelfDo a safety check

If you say I just want to useMember variableIt’s understandable to make the performance reach the optimal solution. This is also a necessary idea in our development. However, you should be careful enough to avoid collapse. The solution is rightweakSelfConduct safety inspection, such as:

self.obj = [[MyObject alloc] init];
__weak typeof(self) weakSelf = self;
[self.obj start:^{
    if (!weakSelf) return; // Security-check here.
    __strong typeof(weakSelf) strongSelf = weakSelf;
    NSLog(@"name: %@", strongSelf->name);
}];

summary

In development, we must avoidCircular referenceagentMemory leak caused by(memory leak)Of course, I believe that for most developers, this basically does not need to think. They can be used to writing corresponding keywords or the middle tier to avoid this problem, but it is estimated that many people will appear in this articlecareless, leading to the problem of online rout.weakNot at allblockAll situations need to be used, such as the animation provided by the systemAPI:

[UIView animateWithDuration:1.0 animations:^{
    NSLog(@"name: %@", self->name);
}];

Excessive unnecessaryweakUsing will make the code redundant and cause unnecessary performance problems!

Personal blog
Demo address
2021-03-18

Recommended Today

Building the basic static page of Vue chat room

design sketch HTML: <template>     <view>         <view>             <scroll-view scroll-y=”true”>                 <div> <!– Message notification — >                     <div>                         <div>2021-12-28 16:50:00</div> < div > XXX processed this work order < / div >                     </div> <!– Left — >                     <!– <div></div> –> <!– Right — >                     <!– <div></div> –>               </div>               <div>                 <div>                     <image src=”../../static/logo.png”>                     <div>                         <div>2021-12-28 16:50:00</div> < […]