IOS development circular reference

Time:2022-1-13

1、 Generation of circular references

1. Description

–: indicates a weak reference.

->: indicates a strong reference.

Circular reference can be simply understood as that object a refers to object B, and object B refers to object A: a – > b – > A. at this time, both parties maintain a reference of the other party at the same time, resulting in that the reference count of both parties is not 0 at any time, and both parties are always unable to release, resulting in memory leakage.

Of course, not only the mutual reference between two objects will form a circular reference, but the mutual reference between multiple objects will eventually form a ring, which will also form a circular reference.

For example: a – > b – > C – >… – > X->B。

2. Hazards

Circular reference is potentially harmful to app, which will lead to excessive memory consumption, memory leakage, poor performance and app flash back.

2、 Three scenarios that are prone to circular references

block 、 delegate 、NSTimer

1、delegate

self.tableView.delegate = self;
If delegate uses the strong modifier, it will form a circular reference: self – > tableview – > delegate – > self.

Therefore, using weak when defining the delegate attribute can solve this problem: self – > tableview — delegate – > self. Tableview and delegate are not strongly referenced, so they cannot form a loop.

The killer mace to avoid delegate circular reference is as simple as crying: when defining the delegate attribute, please use assign (MRC) or weak (ARC). Don’t play with retain or strong.

2.block

(1) Not all blocks generate circular references. We need to judge whether a block generates circular references, for example

//In this way, no circular reference will be generated, because this block is not held by self, but by the class object of uiview. This block has no relationship with self, so self can be used arbitrarily.
[UIView animateWithDuration:0.0 animations:^{
    [self viewDidLoad];
}];

(2) Self – > reachability Manager – > block – > self will generate a circular reference, and Xcode gives a circular reference warning, such as

//Self - > reachability Manager - > block - > self are all circular references
    self.reachabilityManager.stateBlock = ^(int number){
        NSLog(@"%@",self. reachabilityManager);
    };
//Or (there is no explicit "self" in the block. As long as you use what self has in the block, circular references will also appear!)
    self.reachabilityManager.stateBlock = ^(int number){
        NSLog(@"%@",_ reachabilityManager);
    };

(3) The solution to block circular reference is to use__ Weak modifies self, and then uses the modified weak self in the block instead of self:

__weak __typeof(self) weakSelf = self;
[self.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
    NSLog(@"%@",weakSelf.reachabilityManager);
}];
//But only use__ The weak modifier self has one defect:__ Weak may cause memory to reclaim weakself in advance. When nslog() is not executed, weakself will be released, and then print (null) when nslog() is executed.
//Therefore, in order to solve this defect, we need to reuse it inside the block__ Strongmodify weakself:

__weak __typeof(self) weakSelf = self;
[self.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
    __strong __typeof(weakSelf) strongSelf = weakSelf;
    NSLog(@"%@",strongSelf.reachabilityManager);
}];

We find that the above method does solve all problems, but there may be two points we don’t understand:
What is the difference between using weakself and strongself? Why is there no circular reference? This is because the weakself outside the block is to break the circular reference of the ring, while the strongself inside the block is to prevent the early release of the weakself. Strongself is only a local variable in the block, which is recycled after the execution of the block and will no longer cause a circular reference.
What’s the difference between doing this and using weakself? The only difference is that there is an additional strongself. Here, strongself will make the reference count of self + 1, so that self will dealloc only after the block is executed and the local strongself is recycled.
Using the final method can basically solve the circular reference problem of 99% of blocks.
Method for solving circular reference of nstimer

3. NSTimer

1. Start and destroy nstimer at the right time
To solve the circular reference of nstimer, the first method we will think of is to destroy nstimer before oneviewcontroller dealloc, so that the cycle is broken.
The simplest way is to start the nstimer in viewwillappear, and then destroy the nstimer in viewwilldisappear. It appears in pairs. There is absolutely no problem.

-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
     self.time = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(log) userInfo:nil repeats:YES];
}
 
- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [self.time invalidate];
    self.time = nil;
}

2. 52 methods in effective Objective-C

#import <Foundation/Foundation.h>

@interface NSTimer (YPQBlocksSupport)

+ (NSTimer *)ypq_scheduledTimeWithTimeInterval:(NSTimeInterval)interval
                                         block:(void(^)())block
                                       repeats:(BOOL)repeats;

@end


#import "NSTimer+YPQBlocksSupport.h"

@implementation NSTimer (YPQBlocksSupport)


+ (NSTimer *)ypq_scheduledTimeWithTimeInterval:(NSTimeInterval)interval
                                         block:(void(^)())block
                                       repeats:(BOOL)repeats
{
    return [self scheduledTimerWithTimeInterval:interval
                                         target:self
                                       selector:@selector(ypq_blockInvoke:) userInfo:[block copy]
                                        repeats:repeats];
}

- (void)ypq_blockInvoke:(NSTimer *)timer
{
    void (^block)() = timer.userInfo;
    if(block)
    {
        block();
    }
}

@end
__weak ViewController * weakSelf = self;
[NSTimer ypq_scheduledTimeWithTimeInterval:4.0f
                                     block:^{
                                         ViewController * strongSelf = weakSelf;
                                         [strongSelf afterThreeSecondBeginAction];
                                     }
                                   repeats:YES];

The timer retains its target object and repeatedly executes the loop caused by the task. You should really pay attention. In addition, when dealloc, don’t forget to call the invalidate method in the timer.

https://www.jianshu.com/p/41eb11ab4988
https://blog.csdn.net/qq_36557133/article/details/91355258
https://blog.csdn.net/qq_36557133/article/details/87654407
https://www.jianshu.com/p/ddfd1b3c0298

Recommended Today

Interpreting the use of annotations in Ruby

 RubyHanlin AcademiciannotesThe code for is ignored at run time. The single line comment starts with the characters, and they are as follows from to the end of the line: ? 1 2 3 4 5 #!/usr/bin/ruby -w   # This is a single line comment.   puts “Hello, Ruby!” When the above procedure is executed, […]